Load the required libraries

library(cowplot)

Attaching package: ‘cowplot’

The following object is masked from ‘package:patchwork’:

    align_plots

The following object is masked from ‘package:lubridate’:

    stamp

Prepare the Xenium seurat object

start <- Sys.time()

data_dir <- "/project/shared/spatial_data_camp/datasets/DATASET3/XENIUM_5K_LYMPH_NODE"

data <- ReadXenium(data_dir, outs = "matrix", type=c("centroids", "segmentations"))

end <- Sys.time()
paste0("Time to read in the data: ", round(end - start, 2), " mins")
cell_meta_data <- read.csv(file.path(data_dir, "cells.csv.gz"))
annot <- read_csv("/project/shared/spatial_data_camp/HACKATHON/vibe_crew/Xenium_Prime_Human_Lymph_Node_Reactive_FFPE_cell_types.csv")

cell_meta_data <- merge(cell_meta_data, annot, by = "cell_id")
remove(annot)

head(cell_meta_data)
seurat <- CreateSeuratObject(counts = data$matrix[["Gene Expression"]],
                                 assay = "XENIUM",
                                 meta.data = cell_meta_data)

seurat[["Negative.Control.Codeword"]] <- CreateAssayObject(counts = data$matrix[["Negative Control Codeword"]])
seurat[["Negative.Control.Probe"]] <- CreateAssayObject(counts = data$matrix[["Negative Control Probe"]])
seurat[["Unassigned.Codeword"]] <- CreateAssayObject(counts = data$matrix[["Unassigned Codeword"]])

coords <- CreateFOV(coords = list(centroids = CreateCentroids(data$centroids), 
                                  segmentation = CreateSegmentation(data$segmentations)),
                    type = c("segmentation", "centroids"),
                    assay = "XENIUM")
seurat[["LN"]] <- coords
remove(list = list(data, coords, annot, cell_meta_data, start, end))
gc()
#saveRDS(seurat, file = "/project/shared/spatial_data_camp/HACKATHON/vibe_crew/01_xenium_with_annot_and_fov_and_neg_probes.RDS")
seurat <- readRDS(file = "/project/shared/spatial_data_camp/HACKATHON/vibe_crew/01_xenium_with_annot_and_fov_and_neg_probes.RDS")
seurat$Celltype <- seurat$group
seurat$group <- NULL

Subset on the Xenium explorer outline

barcodes <- read_csv("/project/shared/spatial_data_camp/HACKATHON/vibe_crew/LN_whole_cells_stats.csv", skip = 2)
colnames(barcodes) <- c("cell_id", "annotation", "no_transcripts", "area")
seurat$OUTLINE_FILTER <- seurat$cell_id %in% barcodes$cell_id
ImageDimPlot(seurat, group.by="OUTLINE_FILTER")
seurat <- subset(seurat, OUTLINE_FILTER)
ImageDimPlot(seurat, group.by="OUTLINE_FILTER")

Subset on the QC metrics

method <- "MSD" #Select quantiles or MSD

Transcript number

if(method == "quantiles"){
  seurat$TRANSCRIPT_FILTER <- seurat$nCount_XENIUM >= 15
  ImageDimPlot(seurat, group.by="TRANSCRIPT_FILTER")
}

Cell size (min and max)

if(method =="quantiles"){
  seurat[["SIZE_FILTER_SMALL"]] <- seurat$cell_area > quantile(seurat$cell_area, .1)
  seurat[["SIZE_FILTER_LARGE"]] <- seurat$cell_area < quantile(seurat$cell_area, .99)
  ImageDimPlot(seurat, group.by="SIZE_FILTER_SMALL")
  ImageDimPlot(seurat, group.by="SIZE_FILTER_LARGE")
}

Negative probes

seurat[['PROBE_FILTER']] <- seurat$nCount_Unassigned.Codeword == 0 &
                       seurat$nCount_Negative.Control.Codeword == 0 &
                       seurat$nCount_Negative.Control.Probe == 0

Per celltype filtering

#Convert to SCE object
Sce_object <- as.SingleCellExperiment(seurat)

cells_celltype <- list()

for (i in unique(seurat$Celltype)) {
  cells_celltype[[i]] <- Sce_object[,rownames(as.data.frame(colData(Sce_object)) %>% dplyr::filter(Celltype == i))]
}

#Count cutoffs
upper_cut_off_counts_celltype <- list()
lower_cut_off_counts_celltype <- list()
figures <- list()

transcripts_counts_upper_outlier <- isOutlier(Sce_object$nCount_XENIUM, nmads = 2.5, share.mads = F,
                                type="higher", batch=Sce_object$Celltype)

transcripts_counts_lower_outlier <- isOutlier(Sce_object$nCount_XENIUM, nmads = 1.5, share.mads = F,
                                type="lower", batch=Sce_object$Celltype)

for (i in unique(seurat$Celltype)) {
  
  index <- match(i,unique(seurat$Celltype))
  
  upper_cut_off_counts_celltype[[i]] <- attr(transcripts_counts_upper_outlier, "thresholds")['higher', i]
  lower_cut_off_counts_celltype[[i]] <- attr(transcripts_counts_lower_outlier, "thresholds")['lower', i]
  
  data <- data.frame(x=cells_celltype[[i]]$nCount_XENIUM, 
                     Identity = cells_celltype[[i]]$Celltype)
  
  figures[[i]] <- ggplot(data,
                        aes(x = x, fill = as.factor(Identity))) + 
    geom_density(alpha = 0.5) +
    geom_vline(xintercept = lower_cut_off_counts_celltype[[i]], colour="grey", linetype = "longdash") +
    geom_vline(xintercept = upper_cut_off_counts_celltype[[i]], colour="grey", linetype = "longdash") +
    labs(x = expression('log'[10]*'(Library Size)'), title = "Total count density", fill = "Ident") + 
    theme_classic(base_size = 14)
  
} 

combined_plot_reads <- ggpubr::ggarrange(plotlist = figures, ncol = 2, nrow = 2, align = 'h')

# Get the number of unique subpopulations
num_subpopulations <- length(unique(seurat$Celltype))

# Calculate the number of chunks of 4 subpopulations
num_chunks <- ceiling(num_subpopulations / 4)

# Loop over the chunks
for (i in seq_len(num_chunks)) {
  # Calculate the start and end subpopulation for this chunk
  start_subpopulation <- (i - 1) * 4 + 1
  end_subpopulation <- min(i * 4, num_subpopulations)
  
  print(combined_plot_reads[[as.character(i)]])
  
}
#Cell size cutoffs
higher_cell_cut_off_size_celltype <- list()
lower_cell_cut_off_size_celltype <- list()
figures <- list()

cell_size_upper_outlier <- isOutlier(Sce_object$cell_area, nmads = 2.5, share.mads = F,
                                type="higher", batch=Sce_object$Celltype)

cell_size_lower_outlier <- isOutlier(Sce_object$cell_area, nmads = 1.5, share.mads = F,
                                type="lower", batch=Sce_object$Celltype)

for (i in unique(seurat$Celltype)) {
  
  index <- match(i,unique(seurat$Celltype))
  
  higher_cell_cut_off_size_celltype[[i]] <- attr(cell_size_upper_outlier, "thresholds")['higher', i]
  lower_cell_cut_off_size_celltype[[i]] <- attr(cell_size_lower_outlier, "thresholds")['lower', i]
  
  data <- data.frame(x=cells_celltype[[i]]$cell_area, 
                     Identity = cells_celltype[[i]]$Celltype)
  
  figures[[i]] <- ggplot(data,
                        aes(x = x, fill = as.factor(Identity))) + 
    geom_density(alpha = 0.5) +
    geom_vline(xintercept = higher_cell_cut_off_size_celltype[[i]], colour="grey", linetype = "longdash") +
    geom_vline(xintercept = lower_cell_cut_off_size_celltype[[i]], colour="grey", linetype = "longdash") +
    labs(x = expression('Cell Size'), title = "Total size density", fill = "Ident") + 
    theme_classic(base_size = 14)
  
} 

combined_plot_reads <- ggpubr::ggarrange(plotlist = figures, ncol = 2, nrow = 2, align = 'h')

# Get the number of unique subpopulations
num_subpopulations <- length(unique(seurat$Celltype))

# Calculate the number of chunks of 4 subpopulations
num_chunks <- ceiling(num_subpopulations / 4)

# Loop over the chunks
for (i in seq_len(num_chunks)) {
  # Calculate the start and end subpopulation for this chunk
  start_subpopulation <- (i - 1) * 4 + 1
  end_subpopulation <- min(i * 4, num_subpopulations)
  
  print(combined_plot_reads[[as.character(i)]])
  
}

#Cell Filtering

count.drop <- list()
size.drop <- list()

SCE_objects <- list()
SCE_objects.filtered <- list()

for (i in unique(Sce_object$Celltype)) {
  
  SCE_objects[[i]] <- Sce_object[,colnames(cells_celltype[[i]])]
  
}

for (i in unique(Sce_object$Celltype)) {

  count.drop[[i]] <- grep('TRUE', cells_celltype[[i]]$nCount_XENIUM >= lower_cut_off_counts_celltype[[i]] & cells_celltype[[i]]$nCount_XENIUM <= upper_cut_off_counts_celltype[[i]])
  size.drop[[i]] <- grep('TRUE', cells_celltype[[i]]$cell_area >= lower_cell_cut_off_size_celltype[[i]] & cells_celltype[[i]]$cell_area <= higher_cell_cut_off_size_celltype[[i]])
  
}

for (i in unique(Sce_object$Celltype)) {
  
  SCE_objects.filtered[[i]] <- SCE_objects[[i]][,intersect(count.drop[[i]], size.drop[[i]])]
  
}

cells_to_keep <- c()

for (i in unique(Sce_object$Celltype)) {
  
  index <- match(i, unique(Sce_object$Celltype))

  cells_to_keep <- c(cells_to_keep, colnames(SCE_objects.filtered[[i]]))
  
}

Sce_object_filtered <- Sce_object[,cells_to_keep]

# Export number of cells pre and post filtering

print(paste0("Total cells before quality filtering = ",dim(Sce_object)[2]))
print(paste0('Total cells before quality filtering, per sample:'))
table(colData(Sce_object)$Donor_subpopulation)

print(paste0("Total cells remaining after quality filtering = ",dim(Sce_object_filtered)[2]))
print(paste0('Total cells after quality filtering, per sample:'))
table(colData(Sce_object_filtered)$Celltype)

df_number_of_cells <- data.frame(row.names = c('Before_filtering', 'After_filtering'))

for (i in unique(Sce_object$Celltype)) {
  
  index <- match(i, unique(Sce_object$Celltype))

  df_number_of_cells['Before_filtering', i] <- ncol(SCE_objects[[i]])
  df_number_of_cells['After_filtering', i] <- ncol(SCE_objects.filtered[[i]])
  
}

# Export qc metrics pre and post filtering

df_qc <- data.frame()

for (i in unique(Sce_object$Celltype)) {
  
  index <- match(i, unique(Sce_object$Celltype))

  df_qc[i, 'Before_filtering__medianCount'] <- summary(colData(SCE_objects[[i]])$nCount_XENIUM)[[3]]
  df_qc[i, 'After_filtering__medianCount'] <- summary(colData(SCE_objects.filtered[[i]])$nCount_XENIUM)[[3]]
  df_qc[i, 'Before_filtering__medianSize'] <- summary(colData(SCE_objects[[i]])$nCount_XENIUM)[[3]]
  df_qc[i, 'After_filtering__medianSize'] <- summary(colData(SCE_objects.filtered[[i]])$nCount_XENIUM)[[3]]
  
}
msd_barcodes <- colnames(Sce_object_filtered)
write_csv(data.frame("cell_id" = msd_barcodes), file = "msd_barcodes.csv")
remove(Sce_object)
remove(Sce_object_filtered)
remove(SCE_objects)
remove(SCE_objects.filtered)
if(method == "quantiles"){
  seurat$COMBINED_FILTER <- seurat$PROBE_FILTER & seurat$SIZE_FILTER_SMALL & seurat$SIZE_FILTER_LARGE & seurat$TRANSCRIPT_FILTER
} else if (method == "MSD"){
  seurat$COMBINED_FILTER <- seurat$cell_id %in% msd_barcodes
}

ImageDimPlot(seurat, group.by="COMBINED_FILTER")
seurat <- subset(seurat, COMBINED_FILTER)
saveRDS(seurat, "/project/shared/spatial_data_camp/HACKATHON/vibe_crew/02_xenium_after_QC_filtering.RDS")

Answering the question: How does the high number of genes profiled improve analysis resolution?

The approach I’m planning is:

  1. Extract only the IO panel genes from our filtered Seurat object

vs. 

  1. Continue with whole panel

Then perform:

  1. SCTransform
  2. PCA
  3. Clustering
  4. UMAP
  5. Spatial Variable gene expression
  6. Compare list of these variable genes
  7. Compare the clusters that result

Filtered object on basis of IO

Read in the IO panel

Read in the seurat object from before:

seurat <- readRDS("02_xenium_after_QC_filtering.RDS")

For computational space going forward, we are going to take a portion of the LN:

subset_barcodes <- read.csv("LN_subset_cells_stats.csv", skip = 2)
seurat$SMALL_FRAGMENT_FILTER <- seurat$cell_id %in% subset_barcodes$Cell.ID

Now visualise this window:

ImageDimPlot(seurat, group.by="SMALL_FRAGMENT_FILTER")

seurat_fragment <- subset(seurat, SMALL_FRAGMENT_FILTER)
Warning: Not validating Centroids objectsWarning: Not validating Centroids objectsWarning: Not validating FOV objectsWarning: Not validating Centroids objectsWarning: Not validating FOV objectsWarning: Not validating FOV objectsWarning: Not validating FOV objectsWarning: Not validating Seurat objects

Now subset it on the basis of genes:

seurat_io <- seurat_io[,colSums(seurat_io@assays$XENIUM$counts) > 0] # Drops it to 274 x 97958
Warning: Not validating Centroids objectsWarning: Not validating Centroids objectsWarning: Not validating FOV objectsWarning: Not validating Centroids objectsWarning: Not validating FOV objectsWarning: Not validating FOV objectsWarning: Not validating FOV objectsWarning: Not validating Seurat objects

Check out different dimensions of the two Seurat objects:

rbind(`5k` = dim(seurat),
      IO = dim(seurat_io))
   [,1]  [,2]
5k 4624 98152
IO  274 97958

Okay, so there are only 274 genes from the IO panel in the 5k panel (whereas I was expecting 380). How do these distribute?

# Create an io_genes_subset, that is just the intersection with the 5K panel.
io_genes_subset <- io_genes %>% 
  dplyr::filter(Gene %in% rownames(seurat_io))

# Create tables for the short_annotation column for the whole io_genes and the _subset version
table(io_genes_subset$short_annotation) %>% 
  as.data.frame() %>%
  rename(category = "Var1", subset_count = "Freq") %>% 
  left_join(table(io_genes$short_annotation) %>%
              as.data.frame() %>%
              rename(category = "Var1", whole_count = "Freq"),
            .) %>% 
  pivot_longer(cols = contains("count"), names_to = "whole_vs_subset", values_to = "count") %>% 
  ggplot(aes(y = category, x = count, fill = whole_vs_subset)) +
  geom_col(position = position_dodge2())
Joining with `by = join_by(category)`

io_genes_subset %>% 
  ggplot(aes(y = short_annotation)) +
  geom_bar()

And, specifically, these are the gene types that are found in the whole IO panel and not in the subset panel:

unique(io_genes$short_annotation)[which(!(unique(io_genes$short_annotation) %in% unique(io_genes_subset$short_annotation)))]
[1] "Cytoskeleton"    "Receptor Ligand" "Fibroblast"      "TNF Superfamily" "Chief Cell"     
[6] "Intestinal Cell" "Neutrophils"     "Intestinal"     

So the gene types we are missing are mainly non-immune, apart from Neutrophils - which shouldn’t reside in LNs anyway, to be fair.

Transforming the objects

seurat
An object of class Seurat 
12404 features across 98152 samples within 5 assays 
Active assay: SCT (4622 features, 3000 variable features)
 3 layers present: counts, data, scale.data
 4 other assays present: XENIUM, Negative.Control.Codeword, Negative.Control.Probe, Unassigned.Codeword
 1 spatial field of view present: LN

Running PCA

seurat_io
An object of class Seurat 
548 features across 97958 samples within 2 assays 
Active assay: SCT (274 features, 274 variable features)
 3 layers present: counts, data, scale.data
 1 other assay present: XENIUM
 1 dimensional reduction calculated: pca
 1 spatial field of view present: LN

Now the elbow plot:

(ElbowPlot(seurat, 50) + scale_y_continuous(limits = c(1, 6))) + 
  (ElbowPlot(seurat_io, 50) + scale_y_continuous(limits = c(1, 6)))

We will take the first 25 PCs for both objects, but there is less clear inflexion in the smaller panel.

(ImageFeaturePlot(seurat, "PC_1") + scale_fill_viridis_c() + labs(title = "5K")) +
(ImageFeaturePlot(seurat_io, "PC_1") + scale_fill_viridis_c() + labs(title = "IO"))
Warning: No FOV associated with assay 'SCT', using global default FOVScale for fill is already present.
Adding another scale for fill, which will replace the existing scale.Warning: No FOV associated with assay 'SCT', using global default FOVScale for fill is already present.
Adding another scale for fill, which will replace the existing scale.

Run UMAPs

seurat <- FindClusters(seurat, resolution = 0.4)
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 98152
Number of edges: 3013651

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9061
Number of communities: 13
Elapsed time: 61 seconds
seurat_io <- FindClusters(seurat_io, resolution = 0.4)
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 97958
Number of edges: 3217040

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9081
Number of communities: 18
Elapsed time: 78 seconds

Compare UMAP plots and clustering

DimPlot(seurat, group.by = "Celltype", label=T, repel=T) + 
  labs(title = "5K") +
  scale_fill_manual(values = c('Hematopoietic Stem Cell' = '#82A0BC',
                    'Mast Cell' = '#FB7A13',
                    'Neutrophil' = '#06D6A0',
                    "Non Classical Monocyte" = '#00A8E8',
                    'Intermediate Monocyte' = '#007EA7',
                    'Classical Monocyte' = '#003459',
                    "Macrophage" = '#5A189A',
                    'Erythrocyte' = '#C1121F',
                    'Plasma Cell' = '#C1B793',
                    'T Cell' = '#7D676D',
                    "CD4+ αβ Memory T Cell" = '#D5BDAF',
                    "Effector CD8+ αβ T Cell" = '#A9927D',
                    "Regulatory T Cell" = '#E0AFA0',
                    "Effector CD4+ αβ T Cell" = '#C0C0C0',
                    "Naive Thymus Derived CD4+ αβ T Cell" = '#EFCFE3',
                    "CD8+ αβ Memory T Cell" = '#9381FF',
                    "Stromal Cell" = '#FCA311',
                    'Mature Conventional Dendritic Cell' = '#99582A',
                    'CD1c+ Myeloid Dendritic Cell' = '#432818',
                    'CD141+ Myeloid Dendritic Cell' = '#BB9457',
                    'Mature NK T Cell' = '#A4C3B2',
                    'Type I NK T Cell' = '#6B9080',
                    'Plasmacytoid Dendritic Cell' = '#61B4EC',
                    'B Cell' = '#B4D5A1',
                    "Naive B Cell" = '#90BE6D',
                    "Memory B Cell" = '#A7C957',
                    "Innate Lymphoid Cell" = '#FF8FAB',
                    "Endothelial Cell" = '#540B0E'), na.value = '#E6E7E9') +
  theme(legend.position = "none")


DimPlot(seurat_io, group.by = "Celltype", label=T, repel=T) + 
  labs(title = "IO") +
  scale_fill_manual(values = c('Hematopoietic Stem Cell' = '#82A0BC',
                    'Mast Cell' = '#FB7A13',
                    'Neutrophil' = '#06D6A0',
                    "Non Classical Monocyte" = '#00A8E8',
                    'Intermediate Monocyte' = '#007EA7',
                    'Classical Monocyte' = '#003459',
                    "Macrophage" = '#5A189A',
                    'Erythrocyte' = '#C1121F',
                    'Plasma Cell' = '#C1B793',
                    'T Cell' = '#7D676D',
                    "CD4+ αβ Memory T Cell" = '#D5BDAF',
                    "Effector CD8+ αβ T Cell" = '#A9927D',
                    "Regulatory T Cell" = '#E0AFA0',
                    "Effector CD4+ αβ T Cell" = '#C0C0C0',
                    "Naive Thymus Derived CD4+ αβ T Cell" = '#EFCFE3',
                    "CD8+ αβ Memory T Cell" = '#9381FF',
                    "Stromal Cell" = '#FCA311',
                    'Mature Conventional Dendritic Cell' = '#99582A',
                    'CD1c+ Myeloid Dendritic Cell' = '#432818',
                    'CD141+ Myeloid Dendritic Cell' = '#BB9457',
                    'Mature NK T Cell' = '#A4C3B2',
                    'Type I NK T Cell' = '#6B9080',
                    'Plasmacytoid Dendritic Cell' = '#61B4EC',
                    'B Cell' = '#B4D5A1',
                    "Naive B Cell" = '#90BE6D',
                    "Memory B Cell" = '#A7C957',
                    "Innate Lymphoid Cell" = '#FF8FAB',
                    "Endothelial Cell" = '#540B0E'), na.value = '#E6E7E9') +
  theme(legend.position = "none")

ImageDimPlot(seurat, group.by = "Celltype", size=.5) + labs(title = "5K")
Warning: No FOV associated with assay 'SCT', using global default FOV

(ImageDimPlot(seurat, size=.5) + labs(title = "5K")) + 
(ImageDimPlot(seurat_io, size = .5) + labs(title = "IO"))
Warning: No FOV associated with assay 'SCT', using global default FOVWarning: No FOV associated with assay 'SCT', using global default FOV

Repeat the above but better

I’m thinking it would be interesting to do all the above on 10 random genes, 50 random genes, 100 and 200. What can it do?!

seurat
An object of class Seurat 
12404 features across 98152 samples within 5 assays 
Active assay: SCT (4622 features, 3000 variable features)
 3 layers present: counts, data, scale.data
 4 other assays present: XENIUM, Negative.Control.Codeword, Negative.Control.Probe, Unassigned.Codeword
 2 dimensional reductions calculated: pca, umap
 1 spatial field of view present: LN
seurat_1000 <- seurat_1000[,colSums(seurat_1000@assays$XENIUM$counts) > 0]
Warning: Not validating Centroids objectsWarning: Not validating Centroids objectsWarning: Not validating FOV objectsWarning: Not validating Centroids objectsWarning: Not validating FOV objectsWarning: Not validating FOV objectsWarning: Not validating FOV objectsWarning: Not validating Seurat objects

Transforming ten, fifty, etc.

seurat_objects
$ten
An object of class Seurat 
20 features across 39266 samples within 2 assays 
Active assay: SCT (10 features, 10 variable features)
 3 layers present: counts, data, scale.data
 1 other assay present: XENIUM
 1 spatial field of view present: LN

$fifty
An object of class Seurat 
100 features across 83915 samples within 2 assays 
Active assay: SCT (50 features, 50 variable features)
 3 layers present: counts, data, scale.data
 1 other assay present: XENIUM
 1 spatial field of view present: LN

$hundred
An object of class Seurat 
200 features across 94679 samples within 2 assays 
Active assay: SCT (100 features, 100 variable features)
 3 layers present: counts, data, scale.data
 1 other assay present: XENIUM
 1 spatial field of view present: LN

$two_hundred
An object of class Seurat 
400 features across 97387 samples within 2 assays 
Active assay: SCT (200 features, 200 variable features)
 3 layers present: counts, data, scale.data
 1 other assay present: XENIUM
 1 spatial field of view present: LN

$five_hundred
An object of class Seurat 
998 features across 98045 samples within 2 assays 
Active assay: SCT (498 features, 498 variable features)
 3 layers present: counts, data, scale.data
 1 other assay present: XENIUM
 1 spatial field of view present: LN

$one_thousand
An object of class Seurat 
2000 features across 98131 samples within 2 assays 
Active assay: SCT (1000 features, 1000 variable features)
 3 layers present: counts, data, scale.data
 1 other assay present: XENIUM
 1 spatial field of view present: LN

Running PCA

seurat_objects <- seurat_objects %>% 
  map(\(x) RunPCA(x))
Warning: You're computing too large a percentage of total singular values, use a standard svd instead.Warning: Requested number is larger than the number of available items (10). Setting to 10.Warning: Requested number is larger than the number of available items (10). Setting to 10.Warning: Requested number is larger than the number of available items (10). Setting to 10.Warning: Requested number is larger than the number of available items (10). Setting to 10.Warning: Requested number is larger than the number of available items (10). Setting to 10.PC_ 1 
Positive:  HADHA, VPS50, PLXNC1, TSPYL5, SELP 
Negative:  TNFSF8, BMP8B, FSTL3, HOXA5, FBXW7 
PC_ 2 
Positive:  HADHA, TNFSF8, TSPYL5, BMP8B, SELP 
Negative:  VPS50, PLXNC1, FBXW7, HOXA5, FSTL3 
PC_ 3 
Positive:  HADHA, VPS50, TNFSF8, BMP8B, FSTL3 
Negative:  TSPYL5, PLXNC1, SELP, FBXW7, HOXA5 
PC_ 4 
Positive:  PLXNC1, FBXW7, HOXA5, FSTL3, BMP8B 
Negative:  TSPYL5, HADHA, VPS50, TNFSF8, SELP 
PC_ 5 
Positive:  SELP, FBXW7, HOXA5, BMP8B, FSTL3 
Negative:  TSPYL5, PLXNC1, HADHA, TNFSF8, VPS50 
Warning: You're computing too large a percentage of total singular values, use a standard svd instead.Warning: Requested number is larger than the number of available items (50). Setting to 50.Warning: Requested number is larger than the number of available items (50). Setting to 50.Warning: Requested number is larger than the number of available items (50). Setting to 50.Warning: Requested number is larger than the number of available items (50). Setting to 50.Warning: Requested number is larger than the number of available items (50). Setting to 50.PC_ 1 
Positive:  PKM, FCN1, SAPCD2, GABRA3, SULT2A1, INSM1, LEP, PAX7, NEK2, BRINP1 
       ITGA2B, NEK5, BTNL2, REEP6, LIF, PRKAA2, DDX25, PRLR, SEC14L3, NPR3 
       ZSCAN20, FBXO45, HAVCR2, HDAC4, LAIR2 
Negative:  CIITA, DEK, PRKDC, ZNF451, CDKAL1, PPP1R16B, KLHDC2, AKTIP, CEP170, PPP2R3C 
       APOBEC3G, DCAF1, CD2BP2, CDK5RAP1, BSDC1, CCDC137, GNAQ, PRKAG1, SNX19, SLC39A8 
       SIRPB1, PDCD10, GIPC1, PYGL, PRDM10 
PC_ 2 
Positive:  DEK, AKTIP, PRKDC, ZNF451, CDKAL1, KLHDC2, PPP2R3C, SLC39A8, GNAQ, DCAF1 
       GIPC1, CD2BP2, BSDC1, PRKAG1, CEP170, CDK5RAP1, SNX19, HDAC4, APOBEC3G, PDCD10 
       SIRPB1, CCDC137, PYGL, FBXO45, PRDM10 
Negative:  CIITA, PKM, FCN1, SULT2A1, INSM1, GABRA3, BTNL2, PAX7, LEP, BRINP1 
       ITGA2B, DDX25, NEK5, SAPCD2, SEC14L3, NEK2, PRLR, PRKAA2, REEP6, NPR3 
       LIF, ZSCAN20, LAIR2, PPP1R16B, HAVCR2 
PC_ 3 
Positive:  DEK, CIITA, PKM, SAPCD2, FCN1, GABRA3, SULT2A1, LEP, NEK5, ITGA2B 
       BTNL2, PRLR, PAX7, LIF, INSM1, NEK2, REEP6, DDX25, BRINP1, PRKAA2 
       SEC14L3, ZSCAN20, NPR3, FBXO45, HAVCR2 
Negative:  PRKDC, ZNF451, AKTIP, CDKAL1, KLHDC2, PPP1R16B, BSDC1, DCAF1, SNX19, CEP170 
       PRKAG1, PPP2R3C, CDK5RAP1, CD2BP2, SLC39A8, CCDC137, APOBEC3G, SIRPB1, GNAQ, GIPC1 
       HDAC4, PDCD10, LAIR2, PYGL, PRDM10 
PC_ 4 
Positive:  PRKDC, DEK, CIITA, PKM, DDX25, SAPCD2, SULT2A1, BRINP1, ITGA2B, INSM1 
       BTNL2, GABRA3, LEP, PAX7, PRLR, PRKAA2, NEK2, NEK5, FCN1, SEC14L3 
       REEP6, ZSCAN20, LIF, NPR3, LAIR2 
Negative:  ZNF451, AKTIP, CDKAL1, PPP1R16B, KLHDC2, PPP2R3C, APOBEC3G, GNAQ, CEP170, DCAF1 
       BSDC1, SLC39A8, CCDC137, PRKAG1, CD2BP2, GIPC1, CDK5RAP1, SNX19, SIRPB1, HDAC4 
       PDCD10, PYGL, FBXO45, PRDM10, HAVCR2 
PC_ 5 
Positive:  ZNF451, PRKDC, DEK, PPP1R16B, PYGL, PKM, PRKAG1, PDCD10, NPR3, ZSCAN20 
       FCN1, PRKAA2, HAVCR2, REEP6, SAPCD2, GABRA3, PRLR, NEK5, INSM1, DDX25 
       SULT2A1, ITGA2B, PAX7, LIF, FBXO45 
Negative:  AKTIP, CIITA, KLHDC2, PPP2R3C, DCAF1, CDKAL1, CEP170, CCDC137, APOBEC3G, BSDC1 
       GIPC1, CD2BP2, SNX19, SLC39A8, HDAC4, CDK5RAP1, SIRPB1, PRDM10, GNAQ, SEC14L3 
       NEK2, LEP, LAIR2, BRINP1, BTNL2 
Warning: You're computing too large a percentage of total singular values, use a standard svd instead.PC_ 1 
Positive:  TARDBP, TENT5C, ATXN2L, CCND2, KIF2A, MDFIC, WDFY4, TYK2, PI4KA, SMARCA2 
       TNFRSF10A, CDCA7L, ZC3H12D, N4BP2, NAA15, NUDT21, ARRDC3, ECD, MCM3, IKBKG 
       STK40, FAF1, IL2, RPTOR, NKRF, PRKAG2, MCM6, COASY, KXD1, LTK 
Negative:  SIGLEC1, SHC1, THBS1, LTBP2, FUCA1, BCAM, SVIL, NCK1, VSIG4, ZYX 
       MYO1C, RHOC, MYH10, TSBP1, DPYD, ANGPTL2, MAOA, FKBP15, ZC3HAV1L, PPM1H 
       SLC9A1, BNC2, POGLUT2, ETV1, GBX2, PCDHA2, ERAS, SCNN1A, TG, DLK1 
PC_ 2 
Positive:  TENT5C, SIGLEC1, SHC1, TRIB1, FUCA1, YIPF5, MYO1C, LTBP2, GADD45A, THBS1 
       ZC3HAV1L, N4BP2, STK40, MDFIC, LTK, VSIG4, BCAM, MMACHC, PPARGC1A, TSBP1 
       ANGPTL2, NLRP2, NLRP7, COASY, ARRDC3, MAOA, NKX2-1, SCNN1A, MAP3K15, SLC6A5 
Negative:  TARDBP, CCND2, ZYX, ATXN2L, KIF2A, PI4KA, SMARCA2, TNFRSF10A, WDFY4, NDRG1 
       TYK2, RHOC, MCM3, NUDT21, MYD88, ZC3H12D, ECD, NAA15, RPTOR, MCM6 
       DPYD, CDCA7L, NKRF, NUP93, IL2, NCK1, TCFL5, SVIL, PACC1, CENPB 
PC_ 3 
Positive:  THBS1, CCND2, MYO1C, ARRDC3, SLC9A1, MYH10, NDRG1, GADD45A, BNC2, ANGPTL2 
       ZC3HAV1L, TGFB3, RHOC, SVIL, KXD1, BCAM, MCM6, MAOA, ECD, POGLUT2 
       PC, CYTH2, PRKAG2, NUP93, PPARGC1A, MCM3, CENPB, RIPK4, CASQ2, ETV1 
Negative:  SIGLEC1, TARDBP, FUCA1, ATXN2L, KIF2A, WDFY4, LTBP2, VSIG4, SHC1, TENT5C 
       FKBP15, STK40, SMARCA2, TYK2, PI4KA, ZYX, ZC3H12D, NCK1, CDCA7L, PACC1 
       TNFRSF10A, IKBKG, NBN, MYD88, DPYD, TRIB1, MFSD5, RPTOR, TSBP1, CIP2A 
PC_ 4 
Positive:  TARDBP, SIGLEC1, THBS1, TENT5C, FUCA1, LTBP2, VSIG4, ZC3HAV1L, TGFB3, TRIB1 
       BNC2, TH, PCDHA2, FKBP15, RIPK4, PPM1H, PPARGC1A, ERAS, AMH, NKX2-1 
       DLK1, TG, SLC6A5, SLC9A1, GRHL2, TSBP1, CCR3, GBX2, CHRM2, NLRP7 
Negative:  ATXN2L, ZYX, KIF2A, CCND2, SMARCA2, NDRG1, SHC1, MDFIC, PI4KA, TYK2 
       NUDT21, RHOC, NCK1, TNFRSF10A, STK40, MYD88, MCM3, KXD1, NAA15, ECD 
       CENPB, BCAM, NBN, FAF1, ZC3H12D, WDFY4, ARRDC3, NKRF, YIPF5, NUP93 
PC_ 5 
Positive:  SHC1, TARDBP, LTBP2, BCAM, SVIL, NCK1, RHOC, NDRG1, MYO1C, MYH10 
       MAOA, TSBP1, ANGPTL2, ZC3HAV1L, GADD45A, YIPF5, ETV1, POGLUT2, TRIB1, KXD1 
       CENPB, CYTH2, CGRRF1, PRKAG2, SCNN1A, TGFB3, PPM1H, CASQ2, SLC9A1, DNAH10 
Negative:  SIGLEC1, PI4KA, ZYX, THBS1, CCND2, ATXN2L, FUCA1, ARRDC3, KIF2A, SMARCA2 
       WDFY4, MDFIC, TENT5C, ZC3H12D, STK40, FKBP15, CDCA7L, MYD88, PACC1, DPYD 
       N4BP2, VSIG4, ECD, TYK2, MCM6, NAA15, TNFRSF10A, NBN, NKRF, IL2 
PC_ 1 
Positive:  CIITA, CARD11, SLAMF7, AP1B1, LENG8, TLR10, CD4, ILF3, PRKACB, HNRNPF 
       DDX27, SMARCC1, DLEU2, DNAH1, MRFAP1L1, TNFSF8, PTBP1, TAB2, KHDRBS1, MCM3AP 
       CCNDBP1, IKBKE, SNX10, PTPN22, RNF38, MDFIC, USP9Y, TMBIM4, MTA2, NSUN2 
Negative:  CCL19, MAP1LC3A, CP, SYNPO2, ACHE, NRP1, CD302, SLC9A3R2, EGFR, PLSCR1 
       ANGPTL2, ADAMDEC1, TCF4, SOX18, PHLDB2, PTPRM, FLCN, SPECC1, TMEM130, PCBP3 
       FENDRR, PON2, IGFBP2, CKAP4, EGLN3, SIGMAR1, KDR, DSG2, LGALSL, LZTS1 
PC_ 2 
Positive:  CIITA, TLR10, AP1B1, CARD11, CCL19, ILF3, DDX27, SNX10, DLEU2, LENG8 
       CEP170, ZNF395, MCM3AP, ACAP2, AIP, IKBKE, SMARCC1, GIGYF1, KHDRBS1, ADAMDEC1 
       PLSCR1, DNAH1, USP9Y, HNRNPF, RNF38, GTPBP10, FLCN, JRK, PTPRM, PWP1 
Negative:  SLAMF7, CKAP4, DAP, MDFIC, CD4, TCF4, SIGMAR1, PMM2, SLC39A9, CLIC3 
       PTPN22, NRP1, MRFAP1L1, MMADHC, HSF4, CD302, CBLB, PTBP1, CP, USP11 
       TPMT, EMC4, GRAMD1A, TNFSF8, NOLC1, FDPS, CDK9, SPECC1, RIMS3, LIG3 
PC_ 3 
Positive:  CIITA, SLAMF7, TCF4, TLR10, CCL19, AP1B1, CKAP4, DLEU2, SNX10, DAP 
       CLEC12A, CLIC3, USP11, ZNF395, DDX27, EIF4ENIF1, PWP1, PMM2, TMEM192, CARD11 
       MYEF2, POU2F1, AIRE, GSTZ1, ADAMDEC1, ZNF469, SPECC1, CEP170, TMBIM4, SYNPO2 
Negative:  CD4, TNFSF8, GRAMD1A, MRFAP1L1, PRKACB, KHDRBS1, RETREG1, ICOS, IKBKE, CD14 
       CBLB, HNRNPF, MTA2, RNF38, PHLDB2, TAB2, REST, AIP, DNAH1, RANGAP1 
       KDR, NUDT21, PTPRM, FLCN, PTBP1, ILF3, IVNS1ABP, CDK9, LENG8, PYM1 
PC_ 4 
Positive:  CARD11, CCL19, PRKACB, ILF3, TNFSF8, MRFAP1L1, SMARCC1, AIP, TAB2, SLAMF7 
       KHDRBS1, IKBKE, PTPN22, LENG8, REST, TLR10, USP9Y, DNAH1, RETREG1, RNF38 
       DDX27, HNRNPF, MDFIC, PTBP1, ICOS, NOLC1, MTA2, USP11, CBLB, GRAMD1A 
Negative:  TCF4, CD14, NRP1, PHLDB2, KDR, MAP1LC3A, PTPRM, CD4, PLSCR1, CD302 
       SLC9A3R2, SOX18, AP1B1, FLCN, EGLN3, DAP, ADAMDEC1, CP, HSF4, SIGLEC7 
       ANGPTL2, CKAP4, PON2, IGFBP2, PLAT, ACHE, BCL6B, SLC35G2, ACAP2, SLC39A9 
PC_ 5 
Positive:  MAP1LC3A, LENG8, PHLDB2, SLC9A3R2, CP, PTPRM, ILF3, USP11, SMARCC1, GIGYF1 
       PLSCR1, KDR, FLCN, DAP, SOX18, CDK9, GRAMD1A, ZNF395, MCM3AP, CBLB 
       CKAP4, SYNPO2, RNF38, NUDCD1, MRFAP1L1, DLEU2, NUDT21, KHDRBS1, PTBP1, ACHE 
Negative:  CD4, CARD11, PRKACB, CD14, ADAMDEC1, CIITA, CCL19, AP1B1, TCF4, MDFIC 
       CLIC3, CD302, CASS4, TAB2, SIGLEC7, TNFSF8, HNRNPF, AIP, RETREG1, SLAMF7 
       TMBIM4, TPMT, ICOS, YTHDF3, PTPN22, CLEC12A, RIMS3, ABCD1, REST, IKBKE 
PC_ 1 
Positive:  IL16, LBH, CYFIP2, CYLD, OGT, FCRL3, GRK2, FCER2, RUNX3, FCRL1 
       SESN3, MAP4K2, DDX39B, RUBCNL, BACH2, PRPF8, MIAT, IL6R, XRCC6, ADAM8 
       PHB2, SRSF1, FDFT1, POLR2B, PPP1CA, ZNF512, TAB2, CRYBG1, SAFB, CCR4 
Negative:  CCL14, STAB1, LIFR, NOSTRIN, PHLDB2, MMRN2, TJP1, JAM2, SMAD1, MRTFB 
       ADGRL4, CFH, OLFM1, MET, MYOF, TACR1, JAG1, ADGRA2, TIMP2, GJA1 
       ABCD4, CD151, DPYSL2, YAP1, PROS1, THBD, CPXM2, IFI44L, FGFR1, HOXB4 
PC_ 2 
Positive:  FCER2, FCRL1, FCRL3, RUBCNL, TCL1A, CCL14, CR1, PLCG2, KMO, BACH2 
       REL, LBH, ADAM8, CYFIP2, MAP4K2, GRK2, PNOC, LGALS9, NUAK2, RIPOR1 
       FDFT1, FLI1, STAB1, TLR6, ACP5, RBM15, HTT, PHB2, TRAF4, STK40 
Negative:  OGT, IL6R, NUCB2, FNDC3B, PDGFRA, ATF4, CYLD, TIMP2, IL10RA, LDHA 
       MYH9, FGFR1, DIP2A, DST, SSBP3, LAMP3, KLRG1, SND1, CFH, THBD 
       APLNR, HIPK2, ADGRA2, CCR4, SH2D2A, FNDC3A, DDX39B, JAG1, EGFL8, ATOH8 
PC_ 3 
Positive:  NOTCH2, CFP, TIMP2, PDGFRA, DST, NCOA4, FNDC3B, THBD, FGFR1, CR1 
       RUBCNL, CAT, STAB1, PAK1, SND1, IL10RA, LGALS9, FCER2, ATF4, YAP1 
       ADGRA2, FCRL1, RIPOR1, PLCG2, NR4A1, CLEC4A, NAAA, LIFR, MEIS1, EIF2AK3 
Negative:  MYH9, LBH, CCL14, IL16, CYFIP2, CYLD, SESN3, MAP4K2, RUNX3, KLRG1 
       IL6R, LDHA, DDX39B, CCR4, GRK2, GBP5, PPP1CA, NOSTRIN, XRCC6, PRPF8 
       SH2D2A, BACH2, CRYBG1, ZNF512, MTA2, SF3B3, RNF38, FLI1, TAB2, CCT3 
PC_ 4 
Positive:  MYH9, PDGFRA, MRTFB, CFH, FCER2, JAM2, NOSTRIN, MMRN2, ADGRA2, FCRL1 
       OLFM1, LIFR, TJP1, ADGRL4, ATOH8, FGFR1, PROS1, JAG1, THBD, EMP1 
       MET, SYNPO2, APLNR, TIMP2, TCL1A, YAP1, RAI2, RUNX1T1, TACR1, NR4A1 
Negative:  CCL14, IL10RA, CFP, NCOA4, STAB1, CR1, IL6R, GRK2, IL16, CYLD 
       NAAA, OGT, LGALS9, RUNX3, MAP4K2, SESN3, TKT, PAK1, NOTCH2, CYFIP2 
       MYO1F, DIP2A, KLRG1, NUCB2, HEXA, LAMP3, GBP5, DDX39B, PIK3CG, SNX30 
PC_ 5 
Positive:  MYH9, TUBB, NCOA4, CFP, LDHA, TIMP2, TKT, DEK, PAK1, IL10RA 
       XRCC6, LAMP3, LGALS9, CXCL9, NOTCH2, PHB2, CAT, PPP1CA, LBH, UBE2D3 
       CCT3, NAAA, DPYSL2, PDGFRA, CLEC4A, DST, PCNA, THBD, SESN3, SSB 
Negative:  OGT, MIAT, FNDC3B, DDX39B, SND1, ATF4, EIF2AK3, NUCB2, GUSB, CCL14 
       LUC7L, FNDC3A, HIPK2, ZNF512, FCRL3, MAP4K2, PNOC, MON2, FCRL1, PSPC1 
       DIP2A, CASP3, SMN2, PLCG2, VPS13B, ITCH, RBM15, SRSF1, PNPLA2, TBCK 
PC_ 1 
Positive:  CD79A, CXCR4, EEF1G, IKZF3, PTPN6, SEPTIN6, CD27, BCL11A, BLK, TRAF5 
       CARD11, PAX5, RBM38, BTLA, ITGA4, ANKRD44, CD5, SCIMP, SORL1, CNR2 
       FCRL5, AIM2, TC2N, SLAMF6, FCRL2, CD40LG, WDFY4, XPO6, PKM, SESN3 
Negative:  SHANK3, STAB1, FZD4, TIE1, APP, EGFL7, IL3RA, SIGLEC1, VCAM1, NOSTRIN 
       MMP9, TNFSF10, KDR, RAPGEF3, CCL19, AOC3, IL6ST, GNS, AXL, SPTBN1 
       TINAGL1, CFH, ADGRL4, SCARB1, SNX33, HSPB8, TLR4, MAN1A1, FAM107A, TNFRSF1A 
PC_ 2 
Positive:  CD79A, CR2, BCL11A, BLK, SCIMP, VCAM1, PAX5, FCRL5, SIGLEC1, STX7 
       MZB1, MMP9, WDFY4, AIM2, CNR2, FCRL2, DNAJC10, CCDC50, FCGR2B, AXL 
       BLNK, STAB1, MAN1A1, PTPN6, CSF1R, SEMA4B, SMARCB1, TLR4, APP, TRAF5 
Negative:  CCL19, CD5, CD40LG, TC2N, UBASH3A, SIGIRR, SORL1, MAL, EEF1G, PRKCH 
       IL6ST, KLRK1, SELPLG, MVP, RAPGEF6, KIF2A, TUBA4A, IKZF2, GBP1, TCEA3 
       SESN3, CD27, SPTBN1, ACVR2A, FCGBP, CD99, GBP5, NUCB2, LAMP3, PKM 
PC_ 3 
Positive:  MMP9, VCAM1, SIGLEC1, CSF1R, AXL, CCL19, MAN1A1, ITGAX, C4B, IFNGR1 
       PRNP, HCK, NINJ1, TLR4, HNMT, CEBPA, CXCL16, PNPLA6, CD84, CD302 
       NOTCH2NLA, NAAA, HAVCR2, EPB41L3, RXRA, P4HA1, IMPDH1, SELPLG, MKNK1, TKT 
Negative:  SHANK3, FZD4, CD79A, IL3RA, NOSTRIN, EGFL7, TIE1, CR2, RAPGEF3, APP 
       SPTBN1, ADGRL4, BCL11A, AOC3, KDR, CFH, MET, BLK, TINAGL1, PAX5 
       FAM107A, MALL, HSPB8, CCDC50, STAB1, TRAF5, ABCD4, CELSR1, CNR2, SH3BGRL2 
PC_ 4 
Positive:  CR2, SIGLEC1, STAB1, MMP9, CD5, VCAM1, SHANK3, CD40LG, SIGIRR, AXL 
       TC2N, TRAF5, SORL1, CSF1R, CD84, UBASH3A, TLR4, RBM38, IKZF3, TNFRSF14 
       SESN3, RASSF3, PRKCH, CEMIP2, NINJ1, SEPTIN6, PKM, XPO6, TIE1, CXCR4 
Negative:  CCL19, MZB1, PDK1, CCR2, CCDC50, SEL1L, FCRL5, FKBP11, TNFRSF17, CLEC4C 
       RPN1, BMP6, C4B, CKAP4, IRF7, ZFAT, EIF2AK3, MAN1A1, NUCB2, BMI1 
       CP, IL3RA, BCL11A, VEGFC, FRZB, ALG2, SVEP1, TMEM39A, CSNK1E, FBXW7 
PC_ 5 
Positive:  MZB1, PDK1, SEL1L, FKBP11, RPN1, MAN1A1, NUCB2, TNFRSF17, CCR2, BMP6 
       CD27, FCRL5, SIGLEC1, BMI1, SHANK3, CKAP4, EIF2AK3, STAB1, CD5, SELPLG 
       CD40LG, ZBP1, SIGIRR, MMP9, TC2N, FBXW7, UBASH3A, IL6ST, ALG2, AXL 
Negative:  CCL19, CR2, CD79A, BLK, SCIMP, PAX5, C4B, BCL11A, CNR2, FMOD 
       CP, WDFY4, AIM2, SNX33, TRAF5, CCDC50, STX7, ABCC3, FCRL2, PAWR 
       FGFR1, VEGFC, ITIH5, LAMP3, IL7, SEMA4B, ACHE, VCAM1, NFATC4, SREBF2 

ElbowPlot time:

seurat_objects %>% 
  map(\(x) ElbowPlot(x, 50) + labs(title = names(x))) %>% 
  plot_grid(plotlist = .)
Warning: The object only has information for 9 reductionsWarning: The object only has information for 49 reductions

seurat_objects %>% 
  map(\(x) ImageFeaturePlot(x, "PC_1") + scale_fill_viridis_c() + labs(title = names(x)))
Error in `map()`:
ℹ In index: 1.
ℹ With name: ten.
Caused by error in `FetchData()`:
! None of the requested variables were found: PC_1
Backtrace:
  1. seurat_objects %>% ...
  2. purrr::map(...)
  3. purrr:::map_("list", .x, .f, ..., .progress = .progress)
  7. .f(.x[[i]], ...)
  8. Seurat::ImageFeaturePlot(x, "PC_1")
 10. SeuratObject:::FetchData.Seurat(object = object, vars = c(features, split.by[1L]), cells = cells)

Running UMAPs

LS0tCnRpdGxlOiAiVGVhbSBWaWJlIENyZXcgYW5hbHlzaXMiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCiMgTG9hZCB0aGUgcmVxdWlyZWQgbGlicmFyaWVzCmBgYHtyIHNldHVwfQpsaWJyYXJ5KFNldXJhdCkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHNjQ3VzdG9taXplKQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KHBoZWF0bWFwKQpsaWJyYXJ5KG1hdHJpeFN0YXRzKQpsaWJyYXJ5KHNwZGVwKQpsaWJyYXJ5KGdlb2pzb25SKQpsaWJyYXJ5KFNpbmdsZUNlbGxFeHBlcmltZW50KQpsaWJyYXJ5KHNjdXR0bGUpCmxpYnJhcnkoY293cGxvdCkKYGBgCgojIFByZXBhcmUgdGhlIFhlbml1bSBzZXVyYXQgb2JqZWN0CmBgYHtyIExvYWQsIGV2YWw9RkFMU0V9CnN0YXJ0IDwtIFN5cy50aW1lKCkKCmRhdGFfZGlyIDwtICIvcHJvamVjdC9zaGFyZWQvc3BhdGlhbF9kYXRhX2NhbXAvZGF0YXNldHMvREFUQVNFVDMvWEVOSVVNXzVLX0xZTVBIX05PREUiCgpkYXRhIDwtIFJlYWRYZW5pdW0oZGF0YV9kaXIsIG91dHMgPSAibWF0cml4IiwgdHlwZT1jKCJjZW50cm9pZHMiLCAic2VnbWVudGF0aW9ucyIpKQoKZW5kIDwtIFN5cy50aW1lKCkKYGBgCgpgYGB7ciBCZW5jaG1hcmssIGV2YWw9RkFMU0V9CnBhc3RlMCgiVGltZSB0byByZWFkIGluIHRoZSBkYXRhOiAiLCByb3VuZChlbmQgLSBzdGFydCwgMiksICIgbWlucyIpCmBgYAoKYGBge3IgTWV0YWRhdGEsIGV2YWw9RkFMU0V9CmNlbGxfbWV0YV9kYXRhIDwtIHJlYWQuY3N2KGZpbGUucGF0aChkYXRhX2RpciwgImNlbGxzLmNzdi5neiIpKQphbm5vdCA8LSByZWFkX2NzdigiL3Byb2plY3Qvc2hhcmVkL3NwYXRpYWxfZGF0YV9jYW1wL0hBQ0tBVEhPTi92aWJlX2NyZXcvWGVuaXVtX1ByaW1lX0h1bWFuX0x5bXBoX05vZGVfUmVhY3RpdmVfRkZQRV9jZWxsX3R5cGVzLmNzdiIpCgpjZWxsX21ldGFfZGF0YSA8LSBtZXJnZShjZWxsX21ldGFfZGF0YSwgYW5ub3QsIGJ5ID0gImNlbGxfaWQiKQpyZW1vdmUoYW5ub3QpCgpoZWFkKGNlbGxfbWV0YV9kYXRhKQpgYGAKCmBgYHtyLCBldmFsPUZBTFNFfQpzZXVyYXQgPC0gQ3JlYXRlU2V1cmF0T2JqZWN0KGNvdW50cyA9IGRhdGEkbWF0cml4W1siR2VuZSBFeHByZXNzaW9uIl1dLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhc3NheSA9ICJYRU5JVU0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZXRhLmRhdGEgPSBjZWxsX21ldGFfZGF0YSkKCnNldXJhdFtbIk5lZ2F0aXZlLkNvbnRyb2wuQ29kZXdvcmQiXV0gPC0gQ3JlYXRlQXNzYXlPYmplY3QoY291bnRzID0gZGF0YSRtYXRyaXhbWyJOZWdhdGl2ZSBDb250cm9sIENvZGV3b3JkIl1dKQpzZXVyYXRbWyJOZWdhdGl2ZS5Db250cm9sLlByb2JlIl1dIDwtIENyZWF0ZUFzc2F5T2JqZWN0KGNvdW50cyA9IGRhdGEkbWF0cml4W1siTmVnYXRpdmUgQ29udHJvbCBQcm9iZSJdXSkKc2V1cmF0W1siVW5hc3NpZ25lZC5Db2Rld29yZCJdXSA8LSBDcmVhdGVBc3NheU9iamVjdChjb3VudHMgPSBkYXRhJG1hdHJpeFtbIlVuYXNzaWduZWQgQ29kZXdvcmQiXV0pCgpjb29yZHMgPC0gQ3JlYXRlRk9WKGNvb3JkcyA9IGxpc3QoY2VudHJvaWRzID0gQ3JlYXRlQ2VudHJvaWRzKGRhdGEkY2VudHJvaWRzKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWdtZW50YXRpb24gPSBDcmVhdGVTZWdtZW50YXRpb24oZGF0YSRzZWdtZW50YXRpb25zKSksCiAgICAgICAgICAgICAgICAgICAgdHlwZSA9IGMoInNlZ21lbnRhdGlvbiIsICJjZW50cm9pZHMiKSwKICAgICAgICAgICAgICAgICAgICBhc3NheSA9ICJYRU5JVU0iKQpzZXVyYXRbWyJMTiJdXSA8LSBjb29yZHMKYGBgCgpgYGB7ciBEZWxldGUgdW51c2VkIG9iamVjdHMgYW5kIHB1cmdlLCBldmFsPUZBTFNFfQpyZW1vdmUobGlzdCA9IGxpc3QoZGF0YSwgY29vcmRzLCBhbm5vdCwgY2VsbF9tZXRhX2RhdGEsIHN0YXJ0LCBlbmQpKQpnYygpCmBgYAoKYGBge3IgU2F2ZS9sb2FkIHByZS1jb21wdXRlZCBvYmplY3R9CiNzYXZlUkRTKHNldXJhdCwgZmlsZSA9ICIvcHJvamVjdC9zaGFyZWQvc3BhdGlhbF9kYXRhX2NhbXAvSEFDS0FUSE9OL3ZpYmVfY3Jldy8wMV94ZW5pdW1fd2l0aF9hbm5vdF9hbmRfZm92X2FuZF9uZWdfcHJvYmVzLlJEUyIpCnNldXJhdCA8LSByZWFkUkRTKGZpbGUgPSAiL3Byb2plY3Qvc2hhcmVkL3NwYXRpYWxfZGF0YV9jYW1wL0hBQ0tBVEhPTi92aWJlX2NyZXcvMDFfeGVuaXVtX3dpdGhfYW5ub3RfYW5kX2Zvdl9hbmRfbmVnX3Byb2Jlcy5SRFMiKQpgYGAKCmBgYHtyIFJlbmFtZSBjZWxsdHlwZSBjb2x1bW59CnNldXJhdCRDZWxsdHlwZSA8LSBzZXVyYXQkZ3JvdXAKc2V1cmF0JGdyb3VwIDwtIE5VTEwKYGBgCgoKIyBTdWJzZXQgb24gdGhlIFhlbml1bSBleHBsb3JlciBvdXRsaW5lCgpgYGB7ciBSZWFkIGJhcmNvZGVzIGluZm99CmJhcmNvZGVzIDwtIHJlYWRfY3N2KCIvcHJvamVjdC9zaGFyZWQvc3BhdGlhbF9kYXRhX2NhbXAvSEFDS0FUSE9OL3ZpYmVfY3Jldy9MTl93aG9sZV9jZWxsc19zdGF0cy5jc3YiLCBza2lwID0gMikKY29sbmFtZXMoYmFyY29kZXMpIDwtIGMoImNlbGxfaWQiLCAiYW5ub3RhdGlvbiIsICJub190cmFuc2NyaXB0cyIsICJhcmVhIikKYGBgCgoKYGBge3IgVmlzdWFsaXNlIGZpbHRlcn0Kc2V1cmF0JE9VVExJTkVfRklMVEVSIDwtIHNldXJhdCRjZWxsX2lkICVpbiUgYmFyY29kZXMkY2VsbF9pZApJbWFnZURpbVBsb3Qoc2V1cmF0LCBncm91cC5ieT0iT1VUTElORV9GSUxURVIiKQpgYGAKCmBgYHtyIFN1YnNldCwgd2FybmluZz1GQUxTRX0Kc2V1cmF0IDwtIHN1YnNldChzZXVyYXQsIE9VVExJTkVfRklMVEVSKQpJbWFnZURpbVBsb3Qoc2V1cmF0LCBncm91cC5ieT0iT1VUTElORV9GSUxURVIiKQpgYGAKCiMgU3Vic2V0IG9uIHRoZSBRQyBtZXRyaWNzCgpgYGB7ciBTZWxlY3QgZmlsdGVyaW5nIG1ldGhvZH0KbWV0aG9kIDwtICJNU0QiICNTZWxlY3QgcXVhbnRpbGVzIG9yIE1TRApgYGAKCgojIyBUcmFuc2NyaXB0IG51bWJlcgpgYGB7cn0KaWYobWV0aG9kID09ICJxdWFudGlsZXMiKXsKICBzZXVyYXQkVFJBTlNDUklQVF9GSUxURVIgPC0gc2V1cmF0JG5Db3VudF9YRU5JVU0gPj0gMTUKICBJbWFnZURpbVBsb3Qoc2V1cmF0LCBncm91cC5ieT0iVFJBTlNDUklQVF9GSUxURVIiKQp9CmBgYAoKIyMgQ2VsbCBzaXplIChtaW4gYW5kIG1heCkKYGBge3J9CmlmKG1ldGhvZCA9PSJxdWFudGlsZXMiKXsKICBzZXVyYXRbWyJTSVpFX0ZJTFRFUl9TTUFMTCJdXSA8LSBzZXVyYXQkY2VsbF9hcmVhID4gcXVhbnRpbGUoc2V1cmF0JGNlbGxfYXJlYSwgLjEpCiAgc2V1cmF0W1siU0laRV9GSUxURVJfTEFSR0UiXV0gPC0gc2V1cmF0JGNlbGxfYXJlYSA8IHF1YW50aWxlKHNldXJhdCRjZWxsX2FyZWEsIC45OSkKICBJbWFnZURpbVBsb3Qoc2V1cmF0LCBncm91cC5ieT0iU0laRV9GSUxURVJfU01BTEwiKQogIEltYWdlRGltUGxvdChzZXVyYXQsIGdyb3VwLmJ5PSJTSVpFX0ZJTFRFUl9MQVJHRSIpCn0KYGBgCgojIyBOZWdhdGl2ZSBwcm9iZXMKYGBge3J9CnNldXJhdFtbJ1BST0JFX0ZJTFRFUiddXSA8LSBzZXVyYXQkbkNvdW50X1VuYXNzaWduZWQuQ29kZXdvcmQgPT0gMCAmCiAgICAgICAgICAgICAgICAgICAgICAgc2V1cmF0JG5Db3VudF9OZWdhdGl2ZS5Db250cm9sLkNvZGV3b3JkID09IDAgJgogICAgICAgICAgICAgICAgICAgICAgIHNldXJhdCRuQ291bnRfTmVnYXRpdmUuQ29udHJvbC5Qcm9iZSA9PSAwCmBgYAoKIyMgUGVyIGNlbGx0eXBlIGZpbHRlcmluZwoKYGBge3J9CiNDb252ZXJ0IHRvIFNDRSBvYmplY3QKU2NlX29iamVjdCA8LSBhcy5TaW5nbGVDZWxsRXhwZXJpbWVudChzZXVyYXQpCgpjZWxsc19jZWxsdHlwZSA8LSBsaXN0KCkKCmZvciAoaSBpbiB1bmlxdWUoc2V1cmF0JENlbGx0eXBlKSkgewogIGNlbGxzX2NlbGx0eXBlW1tpXV0gPC0gU2NlX29iamVjdFsscm93bmFtZXMoYXMuZGF0YS5mcmFtZShjb2xEYXRhKFNjZV9vYmplY3QpKSAlPiUgZHBseXI6OmZpbHRlcihDZWxsdHlwZSA9PSBpKSldCn0KCiNDb3VudCBjdXRvZmZzCnVwcGVyX2N1dF9vZmZfY291bnRzX2NlbGx0eXBlIDwtIGxpc3QoKQpsb3dlcl9jdXRfb2ZmX2NvdW50c19jZWxsdHlwZSA8LSBsaXN0KCkKZmlndXJlcyA8LSBsaXN0KCkKCnRyYW5zY3JpcHRzX2NvdW50c191cHBlcl9vdXRsaWVyIDwtIGlzT3V0bGllcihTY2Vfb2JqZWN0JG5Db3VudF9YRU5JVU0sIG5tYWRzID0gMi41LCBzaGFyZS5tYWRzID0gRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlPSJoaWdoZXIiLCBiYXRjaD1TY2Vfb2JqZWN0JENlbGx0eXBlKQoKdHJhbnNjcmlwdHNfY291bnRzX2xvd2VyX291dGxpZXIgPC0gaXNPdXRsaWVyKFNjZV9vYmplY3QkbkNvdW50X1hFTklVTSwgbm1hZHMgPSAxLjUsIHNoYXJlLm1hZHMgPSBGLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU9Imxvd2VyIiwgYmF0Y2g9U2NlX29iamVjdCRDZWxsdHlwZSkKCmZvciAoaSBpbiB1bmlxdWUoc2V1cmF0JENlbGx0eXBlKSkgewogIAogIGluZGV4IDwtIG1hdGNoKGksdW5pcXVlKHNldXJhdCRDZWxsdHlwZSkpCiAgCiAgdXBwZXJfY3V0X29mZl9jb3VudHNfY2VsbHR5cGVbW2ldXSA8LSBhdHRyKHRyYW5zY3JpcHRzX2NvdW50c191cHBlcl9vdXRsaWVyLCAidGhyZXNob2xkcyIpWydoaWdoZXInLCBpXQogIGxvd2VyX2N1dF9vZmZfY291bnRzX2NlbGx0eXBlW1tpXV0gPC0gYXR0cih0cmFuc2NyaXB0c19jb3VudHNfbG93ZXJfb3V0bGllciwgInRocmVzaG9sZHMiKVsnbG93ZXInLCBpXQogIAogIGRhdGEgPC0gZGF0YS5mcmFtZSh4PWNlbGxzX2NlbGx0eXBlW1tpXV0kbkNvdW50X1hFTklVTSwgCiAgICAgICAgICAgICAgICAgICAgIElkZW50aXR5ID0gY2VsbHNfY2VsbHR5cGVbW2ldXSRDZWxsdHlwZSkKICAKICBmaWd1cmVzW1tpXV0gPC0gZ2dwbG90KGRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0geCwgZmlsbCA9IGFzLmZhY3RvcihJZGVudGl0eSkpKSArIAogICAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC41KSArCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBsb3dlcl9jdXRfb2ZmX2NvdW50c19jZWxsdHlwZVtbaV1dLCBjb2xvdXI9ImdyZXkiLCBsaW5ldHlwZSA9ICJsb25nZGFzaCIpICsKICAgIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IHVwcGVyX2N1dF9vZmZfY291bnRzX2NlbGx0eXBlW1tpXV0sIGNvbG91cj0iZ3JleSIsIGxpbmV0eXBlID0gImxvbmdkYXNoIikgKwogICAgbGFicyh4ID0gZXhwcmVzc2lvbignbG9nJ1sxMF0qJyhMaWJyYXJ5IFNpemUpJyksIHRpdGxlID0gIlRvdGFsIGNvdW50IGRlbnNpdHkiLCBmaWxsID0gIklkZW50IikgKyAKICAgIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTQpCiAgCn0gCgpjb21iaW5lZF9wbG90X3JlYWRzIDwtIGdncHVicjo6Z2dhcnJhbmdlKHBsb3RsaXN0ID0gZmlndXJlcywgbmNvbCA9IDIsIG5yb3cgPSAyLCBhbGlnbiA9ICdoJykKCiMgR2V0IHRoZSBudW1iZXIgb2YgdW5pcXVlIHN1YnBvcHVsYXRpb25zCm51bV9zdWJwb3B1bGF0aW9ucyA8LSBsZW5ndGgodW5pcXVlKHNldXJhdCRDZWxsdHlwZSkpCgojIENhbGN1bGF0ZSB0aGUgbnVtYmVyIG9mIGNodW5rcyBvZiA0IHN1YnBvcHVsYXRpb25zCm51bV9jaHVua3MgPC0gY2VpbGluZyhudW1fc3VicG9wdWxhdGlvbnMgLyA0KQoKIyBMb29wIG92ZXIgdGhlIGNodW5rcwpmb3IgKGkgaW4gc2VxX2xlbihudW1fY2h1bmtzKSkgewogICMgQ2FsY3VsYXRlIHRoZSBzdGFydCBhbmQgZW5kIHN1YnBvcHVsYXRpb24gZm9yIHRoaXMgY2h1bmsKICBzdGFydF9zdWJwb3B1bGF0aW9uIDwtIChpIC0gMSkgKiA0ICsgMQogIGVuZF9zdWJwb3B1bGF0aW9uIDwtIG1pbihpICogNCwgbnVtX3N1YnBvcHVsYXRpb25zKQogIAogIHByaW50KGNvbWJpbmVkX3Bsb3RfcmVhZHNbW2FzLmNoYXJhY3RlcihpKV1dKQogIAp9CiNDZWxsIHNpemUgY3V0b2ZmcwpoaWdoZXJfY2VsbF9jdXRfb2ZmX3NpemVfY2VsbHR5cGUgPC0gbGlzdCgpCmxvd2VyX2NlbGxfY3V0X29mZl9zaXplX2NlbGx0eXBlIDwtIGxpc3QoKQpmaWd1cmVzIDwtIGxpc3QoKQoKY2VsbF9zaXplX3VwcGVyX291dGxpZXIgPC0gaXNPdXRsaWVyKFNjZV9vYmplY3QkY2VsbF9hcmVhLCBubWFkcyA9IDIuNSwgc2hhcmUubWFkcyA9IEYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZT0iaGlnaGVyIiwgYmF0Y2g9U2NlX29iamVjdCRDZWxsdHlwZSkKCmNlbGxfc2l6ZV9sb3dlcl9vdXRsaWVyIDwtIGlzT3V0bGllcihTY2Vfb2JqZWN0JGNlbGxfYXJlYSwgbm1hZHMgPSAxLjUsIHNoYXJlLm1hZHMgPSBGLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGU9Imxvd2VyIiwgYmF0Y2g9U2NlX29iamVjdCRDZWxsdHlwZSkKCmZvciAoaSBpbiB1bmlxdWUoc2V1cmF0JENlbGx0eXBlKSkgewogIAogIGluZGV4IDwtIG1hdGNoKGksdW5pcXVlKHNldXJhdCRDZWxsdHlwZSkpCiAgCiAgaGlnaGVyX2NlbGxfY3V0X29mZl9zaXplX2NlbGx0eXBlW1tpXV0gPC0gYXR0cihjZWxsX3NpemVfdXBwZXJfb3V0bGllciwgInRocmVzaG9sZHMiKVsnaGlnaGVyJywgaV0KICBsb3dlcl9jZWxsX2N1dF9vZmZfc2l6ZV9jZWxsdHlwZVtbaV1dIDwtIGF0dHIoY2VsbF9zaXplX2xvd2VyX291dGxpZXIsICJ0aHJlc2hvbGRzIilbJ2xvd2VyJywgaV0KICAKICBkYXRhIDwtIGRhdGEuZnJhbWUoeD1jZWxsc19jZWxsdHlwZVtbaV1dJGNlbGxfYXJlYSwgCiAgICAgICAgICAgICAgICAgICAgIElkZW50aXR5ID0gY2VsbHNfY2VsbHR5cGVbW2ldXSRDZWxsdHlwZSkKICAKICBmaWd1cmVzW1tpXV0gPC0gZ2dwbG90KGRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0geCwgZmlsbCA9IGFzLmZhY3RvcihJZGVudGl0eSkpKSArIAogICAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC41KSArCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBoaWdoZXJfY2VsbF9jdXRfb2ZmX3NpemVfY2VsbHR5cGVbW2ldXSwgY29sb3VyPSJncmV5IiwgbGluZXR5cGUgPSAibG9uZ2Rhc2giKSArCiAgICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBsb3dlcl9jZWxsX2N1dF9vZmZfc2l6ZV9jZWxsdHlwZVtbaV1dLCBjb2xvdXI9ImdyZXkiLCBsaW5ldHlwZSA9ICJsb25nZGFzaCIpICsKICAgIGxhYnMoeCA9IGV4cHJlc3Npb24oJ0NlbGwgU2l6ZScpLCB0aXRsZSA9ICJUb3RhbCBzaXplIGRlbnNpdHkiLCBmaWxsID0gIklkZW50IikgKyAKICAgIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTQpCiAgCn0gCgpjb21iaW5lZF9wbG90X3JlYWRzIDwtIGdncHVicjo6Z2dhcnJhbmdlKHBsb3RsaXN0ID0gZmlndXJlcywgbmNvbCA9IDIsIG5yb3cgPSAyLCBhbGlnbiA9ICdoJykKCiMgR2V0IHRoZSBudW1iZXIgb2YgdW5pcXVlIHN1YnBvcHVsYXRpb25zCm51bV9zdWJwb3B1bGF0aW9ucyA8LSBsZW5ndGgodW5pcXVlKHNldXJhdCRDZWxsdHlwZSkpCgojIENhbGN1bGF0ZSB0aGUgbnVtYmVyIG9mIGNodW5rcyBvZiA0IHN1YnBvcHVsYXRpb25zCm51bV9jaHVua3MgPC0gY2VpbGluZyhudW1fc3VicG9wdWxhdGlvbnMgLyA0KQoKIyBMb29wIG92ZXIgdGhlIGNodW5rcwpmb3IgKGkgaW4gc2VxX2xlbihudW1fY2h1bmtzKSkgewogICMgQ2FsY3VsYXRlIHRoZSBzdGFydCBhbmQgZW5kIHN1YnBvcHVsYXRpb24gZm9yIHRoaXMgY2h1bmsKICBzdGFydF9zdWJwb3B1bGF0aW9uIDwtIChpIC0gMSkgKiA0ICsgMQogIGVuZF9zdWJwb3B1bGF0aW9uIDwtIG1pbihpICogNCwgbnVtX3N1YnBvcHVsYXRpb25zKQogIAogIHByaW50KGNvbWJpbmVkX3Bsb3RfcmVhZHNbW2FzLmNoYXJhY3RlcihpKV1dKQogIAp9CgojQ2VsbCBGaWx0ZXJpbmcKCmNvdW50LmRyb3AgPC0gbGlzdCgpCnNpemUuZHJvcCA8LSBsaXN0KCkKClNDRV9vYmplY3RzIDwtIGxpc3QoKQpTQ0Vfb2JqZWN0cy5maWx0ZXJlZCA8LSBsaXN0KCkKCmZvciAoaSBpbiB1bmlxdWUoU2NlX29iamVjdCRDZWxsdHlwZSkpIHsKICAKICBTQ0Vfb2JqZWN0c1tbaV1dIDwtIFNjZV9vYmplY3RbLGNvbG5hbWVzKGNlbGxzX2NlbGx0eXBlW1tpXV0pXQogIAp9Cgpmb3IgKGkgaW4gdW5pcXVlKFNjZV9vYmplY3QkQ2VsbHR5cGUpKSB7CgogIGNvdW50LmRyb3BbW2ldXSA8LSBncmVwKCdUUlVFJywgY2VsbHNfY2VsbHR5cGVbW2ldXSRuQ291bnRfWEVOSVVNID49IGxvd2VyX2N1dF9vZmZfY291bnRzX2NlbGx0eXBlW1tpXV0gJiBjZWxsc19jZWxsdHlwZVtbaV1dJG5Db3VudF9YRU5JVU0gPD0gdXBwZXJfY3V0X29mZl9jb3VudHNfY2VsbHR5cGVbW2ldXSkKICBzaXplLmRyb3BbW2ldXSA8LSBncmVwKCdUUlVFJywgY2VsbHNfY2VsbHR5cGVbW2ldXSRjZWxsX2FyZWEgPj0gbG93ZXJfY2VsbF9jdXRfb2ZmX3NpemVfY2VsbHR5cGVbW2ldXSAmIGNlbGxzX2NlbGx0eXBlW1tpXV0kY2VsbF9hcmVhIDw9IGhpZ2hlcl9jZWxsX2N1dF9vZmZfc2l6ZV9jZWxsdHlwZVtbaV1dKQogIAp9Cgpmb3IgKGkgaW4gdW5pcXVlKFNjZV9vYmplY3QkQ2VsbHR5cGUpKSB7CiAgCiAgU0NFX29iamVjdHMuZmlsdGVyZWRbW2ldXSA8LSBTQ0Vfb2JqZWN0c1tbaV1dWyxpbnRlcnNlY3QoY291bnQuZHJvcFtbaV1dLCBzaXplLmRyb3BbW2ldXSldCiAgCn0KCmNlbGxzX3RvX2tlZXAgPC0gYygpCgpmb3IgKGkgaW4gdW5pcXVlKFNjZV9vYmplY3QkQ2VsbHR5cGUpKSB7CiAgCiAgaW5kZXggPC0gbWF0Y2goaSwgdW5pcXVlKFNjZV9vYmplY3QkQ2VsbHR5cGUpKQoKICBjZWxsc190b19rZWVwIDwtIGMoY2VsbHNfdG9fa2VlcCwgY29sbmFtZXMoU0NFX29iamVjdHMuZmlsdGVyZWRbW2ldXSkpCiAgCn0KClNjZV9vYmplY3RfZmlsdGVyZWQgPC0gU2NlX29iamVjdFssY2VsbHNfdG9fa2VlcF0KCiMgRXhwb3J0IG51bWJlciBvZiBjZWxscyBwcmUgYW5kIHBvc3QgZmlsdGVyaW5nCgpwcmludChwYXN0ZTAoIlRvdGFsIGNlbGxzIGJlZm9yZSBxdWFsaXR5IGZpbHRlcmluZyA9ICIsZGltKFNjZV9vYmplY3QpWzJdKSkKcHJpbnQocGFzdGUwKCdUb3RhbCBjZWxscyBiZWZvcmUgcXVhbGl0eSBmaWx0ZXJpbmcsIHBlciBzYW1wbGU6JykpCnRhYmxlKGNvbERhdGEoU2NlX29iamVjdCkkRG9ub3Jfc3VicG9wdWxhdGlvbikKCnByaW50KHBhc3RlMCgiVG90YWwgY2VsbHMgcmVtYWluaW5nIGFmdGVyIHF1YWxpdHkgZmlsdGVyaW5nID0gIixkaW0oU2NlX29iamVjdF9maWx0ZXJlZClbMl0pKQpwcmludChwYXN0ZTAoJ1RvdGFsIGNlbGxzIGFmdGVyIHF1YWxpdHkgZmlsdGVyaW5nLCBwZXIgc2FtcGxlOicpKQp0YWJsZShjb2xEYXRhKFNjZV9vYmplY3RfZmlsdGVyZWQpJENlbGx0eXBlKQoKZGZfbnVtYmVyX29mX2NlbGxzIDwtIGRhdGEuZnJhbWUocm93Lm5hbWVzID0gYygnQmVmb3JlX2ZpbHRlcmluZycsICdBZnRlcl9maWx0ZXJpbmcnKSkKCmZvciAoaSBpbiB1bmlxdWUoU2NlX29iamVjdCRDZWxsdHlwZSkpIHsKICAKICBpbmRleCA8LSBtYXRjaChpLCB1bmlxdWUoU2NlX29iamVjdCRDZWxsdHlwZSkpCgogIGRmX251bWJlcl9vZl9jZWxsc1snQmVmb3JlX2ZpbHRlcmluZycsIGldIDwtIG5jb2woU0NFX29iamVjdHNbW2ldXSkKICBkZl9udW1iZXJfb2ZfY2VsbHNbJ0FmdGVyX2ZpbHRlcmluZycsIGldIDwtIG5jb2woU0NFX29iamVjdHMuZmlsdGVyZWRbW2ldXSkKICAKfQoKIyBFeHBvcnQgcWMgbWV0cmljcyBwcmUgYW5kIHBvc3QgZmlsdGVyaW5nCgpkZl9xYyA8LSBkYXRhLmZyYW1lKCkKCmZvciAoaSBpbiB1bmlxdWUoU2NlX29iamVjdCRDZWxsdHlwZSkpIHsKICAKICBpbmRleCA8LSBtYXRjaChpLCB1bmlxdWUoU2NlX29iamVjdCRDZWxsdHlwZSkpCgogIGRmX3FjW2ksICdCZWZvcmVfZmlsdGVyaW5nX19tZWRpYW5Db3VudCddIDwtIHN1bW1hcnkoY29sRGF0YShTQ0Vfb2JqZWN0c1tbaV1dKSRuQ291bnRfWEVOSVVNKVtbM11dCiAgZGZfcWNbaSwgJ0FmdGVyX2ZpbHRlcmluZ19fbWVkaWFuQ291bnQnXSA8LSBzdW1tYXJ5KGNvbERhdGEoU0NFX29iamVjdHMuZmlsdGVyZWRbW2ldXSkkbkNvdW50X1hFTklVTSlbWzNdXQogIGRmX3FjW2ksICdCZWZvcmVfZmlsdGVyaW5nX19tZWRpYW5TaXplJ10gPC0gc3VtbWFyeShjb2xEYXRhKFNDRV9vYmplY3RzW1tpXV0pJG5Db3VudF9YRU5JVU0pW1szXV0KICBkZl9xY1tpLCAnQWZ0ZXJfZmlsdGVyaW5nX19tZWRpYW5TaXplJ10gPC0gc3VtbWFyeShjb2xEYXRhKFNDRV9vYmplY3RzLmZpbHRlcmVkW1tpXV0pJG5Db3VudF9YRU5JVU0pW1szXV0KICAKfQpgYGAKCmBgYHtyIFNhdmUgdGhlIHJlc3VsdH0KbXNkX2JhcmNvZGVzIDwtIGNvbG5hbWVzKFNjZV9vYmplY3RfZmlsdGVyZWQpCndyaXRlX2NzdihkYXRhLmZyYW1lKCJjZWxsX2lkIiA9IG1zZF9iYXJjb2RlcyksIGZpbGUgPSAibXNkX2JhcmNvZGVzLmNzdiIpCmBgYAoKYGBge3IgQ2xlYW4tdXB9CnJlbW92ZShTY2Vfb2JqZWN0KQpyZW1vdmUoU2NlX29iamVjdF9maWx0ZXJlZCkKcmVtb3ZlKFNDRV9vYmplY3RzKQpyZW1vdmUoU0NFX29iamVjdHMuZmlsdGVyZWQpCmBgYAoKCmBgYHtyIEZpbHRlciB0aGUgb2JqZWN0fQppZihtZXRob2QgPT0gInF1YW50aWxlcyIpewogIHNldXJhdCRDT01CSU5FRF9GSUxURVIgPC0gc2V1cmF0JFBST0JFX0ZJTFRFUiAmIHNldXJhdCRTSVpFX0ZJTFRFUl9TTUFMTCAmIHNldXJhdCRTSVpFX0ZJTFRFUl9MQVJHRSAmIHNldXJhdCRUUkFOU0NSSVBUX0ZJTFRFUgp9IGVsc2UgaWYgKG1ldGhvZCA9PSAiTVNEIil7CiAgc2V1cmF0JENPTUJJTkVEX0ZJTFRFUiA8LSBzZXVyYXQkY2VsbF9pZCAlaW4lIG1zZF9iYXJjb2Rlcwp9CgpJbWFnZURpbVBsb3Qoc2V1cmF0LCBncm91cC5ieT0iQ09NQklORURfRklMVEVSIikKYGBgCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQpzZXVyYXQgPC0gc3Vic2V0KHNldXJhdCwgQ09NQklORURfRklMVEVSKQpzYXZlUkRTKHNldXJhdCwgIi9wcm9qZWN0L3NoYXJlZC9zcGF0aWFsX2RhdGFfY2FtcC9IQUNLQVRIT04vdmliZV9jcmV3LzAyX3hlbml1bV9hZnRlcl9RQ19maWx0ZXJpbmcuUkRTIikKYGBgCgojIEFuc3dlcmluZyB0aGUgcXVlc3Rpb246IEhvdyBkb2VzIHRoZSBoaWdoIG51bWJlciBvZiBnZW5lcyBwcm9maWxlZCBpbXByb3ZlIGFuYWx5c2lzIHJlc29sdXRpb24/CgpUaGUgYXBwcm9hY2ggSSdtIHBsYW5uaW5nIGlzOgoKMS4gRXh0cmFjdCBvbmx5IHRoZSBJTyBwYW5lbCBnZW5lcyBmcm9tIG91ciBmaWx0ZXJlZCBTZXVyYXQgb2JqZWN0Cgp2cy4gCgoyLiBDb250aW51ZSB3aXRoIHdob2xlIHBhbmVsCgpUaGVuIHBlcmZvcm06CgoxLiBTQ1RyYW5zZm9ybQoyLiBQQ0EKMy4gQ2x1c3RlcmluZyAKNC4gVU1BUAo1LiBTcGF0aWFsIFZhcmlhYmxlIGdlbmUgZXhwcmVzc2lvbgo2LiBDb21wYXJlIGxpc3Qgb2YgdGhlc2UgdmFyaWFibGUgZ2VuZXMKNy4gQ29tcGFyZSB0aGUgY2x1c3RlcnMgdGhhdCByZXN1bHQKCiMjIEZpbHRlcmVkIG9iamVjdCBvbiBiYXNpcyBvZiBJTwoKUmVhZCBpbiB0aGUgSU8gcGFuZWwKCmBgYHtyfQppb19nZW5lcyA8LSByZWFkLmNzdigiL3Byb2plY3QvYnJhczI0MzAvU0hBUkVEL3NwYXRpYWxfZGF0YV9jYW1wL0hBQ0tBVEhPTi92aWJlX2NyZXcvWGVuaXVtX2hJT192MV9tZXRhZGF0YS5jc3YiKSAlPiUgCiAgbXV0YXRlKHNob3J0X2Fubm90YXRpb24gPSBnc3ViKCI7LioiLCAiIiwgQW5ub3RhdGlvbiksCiAgICAgICAgIHNob3J0X2Fubm90YXRpb24gPSBjYXNlX21hdGNoKHNob3J0X2Fubm90YXRpb24sIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQiBjZWxsIiB+ICJCIENlbGwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVCBjZWxsIiB+ICJUIENlbGwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAuZGVmYXVsdCA9IHNob3J0X2Fubm90YXRpb24pKSAKCmBgYAoKUmVhZCBpbiB0aGUgc2V1cmF0IG9iamVjdCBmcm9tIGJlZm9yZToKCmBgYHtyfQpzZXVyYXQgPC0gcmVhZFJEUygiMDJfeGVuaXVtX2FmdGVyX1FDX2ZpbHRlcmluZy5SRFMiKQpgYGAKCkZvciBjb21wdXRhdGlvbmFsIHNwYWNlIGdvaW5nIGZvcndhcmQsIHdlIGFyZSBnb2luZyB0byB0YWtlIGEgcG9ydGlvbiBvZiB0aGUgTE46CgpgYGB7cn0Kc3Vic2V0X2JhcmNvZGVzIDwtIHJlYWQuY3N2KCJMTl9zdWJzZXRfY2VsbHNfc3RhdHMuY3N2Iiwgc2tpcCA9IDIpCnNldXJhdCRTTUFMTF9GUkFHTUVOVF9GSUxURVIgPC0gc2V1cmF0JGNlbGxfaWQgJWluJSBzdWJzZXRfYmFyY29kZXMkQ2VsbC5JRApgYGAKCk5vdyB2aXN1YWxpc2UgdGhpcyB3aW5kb3c6CgpgYGB7cn0KSW1hZ2VEaW1QbG90KHNldXJhdCwgZ3JvdXAuYnk9IlNNQUxMX0ZSQUdNRU5UX0ZJTFRFUiIpCmBgYAoKYGBge3J9CnNldXJhdF9mcmFnbWVudCA8LSBzdWJzZXQoc2V1cmF0LCBTTUFMTF9GUkFHTUVOVF9GSUxURVIpCmBgYAoKCk5vdyBzdWJzZXQgaXQgb24gdGhlIGJhc2lzIG9mIGdlbmVzOgoKYGBge3J9CnNldXJhdF9pbyA8LSBzZXVyYXRfZnJhZ21lbnRbcm93bmFtZXMoc2V1cmF0X2ZyYWdtZW50KSAlaW4lIGlvX2dlbmVzJEdlbmUsXSAjIERyb3BzIGl0IHRvIDI3NCB4IDk4MTUyCnNldXJhdF9pbyA8LSBzZXVyYXRfaW9bLGNvbFN1bXMoc2V1cmF0X2lvQGFzc2F5cyRYRU5JVU0kY291bnRzKSA+IDBdICMgRHJvcHMgaXQgdG8gMjc0IHggOTc5NTgKYGBgCgpDaGVjayBvdXQgZGlmZmVyZW50IGRpbWVuc2lvbnMgb2YgdGhlIHR3byBTZXVyYXQgb2JqZWN0czoKCmBgYHtyfQpyYmluZChgNWtgID0gZGltKHNldXJhdCksCiAgICAgIElPID0gZGltKHNldXJhdF9pbykpCmBgYAoKT2theSwgc28gdGhlcmUgYXJlIG9ubHkgMjc0IGdlbmVzIGZyb20gdGhlIElPIHBhbmVsIGluIHRoZSA1ayBwYW5lbCAod2hlcmVhcyBJIHdhcyBleHBlY3RpbmcgMzgwKS4gSG93IGRvIHRoZXNlIGRpc3RyaWJ1dGU/CgpgYGB7cn0KIyBDcmVhdGUgYW4gaW9fZ2VuZXNfc3Vic2V0LCB0aGF0IGlzIGp1c3QgdGhlIGludGVyc2VjdGlvbiB3aXRoIHRoZSA1SyBwYW5lbC4KaW9fZ2VuZXNfc3Vic2V0IDwtIGlvX2dlbmVzICU+JSAKICBkcGx5cjo6ZmlsdGVyKEdlbmUgJWluJSByb3duYW1lcyhzZXVyYXRfaW8pKQoKIyBDcmVhdGUgdGFibGVzIGZvciB0aGUgc2hvcnRfYW5ub3RhdGlvbiBjb2x1bW4gZm9yIHRoZSB3aG9sZSBpb19nZW5lcyBhbmQgdGhlIF9zdWJzZXQgdmVyc2lvbgp0YWJsZShpb19nZW5lc19zdWJzZXQkc2hvcnRfYW5ub3RhdGlvbikgJT4lIAogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICByZW5hbWUoY2F0ZWdvcnkgPSAiVmFyMSIsIHN1YnNldF9jb3VudCA9ICJGcmVxIikgJT4lIAogIGxlZnRfam9pbih0YWJsZShpb19nZW5lcyRzaG9ydF9hbm5vdGF0aW9uKSAlPiUKICAgICAgICAgICAgICBhcy5kYXRhLmZyYW1lKCkgJT4lCiAgICAgICAgICAgICAgcmVuYW1lKGNhdGVnb3J5ID0gIlZhcjEiLCB3aG9sZV9jb3VudCA9ICJGcmVxIiksCiAgICAgICAgICAgIC4pICU+JSAKICBwaXZvdF9sb25nZXIoY29scyA9IGNvbnRhaW5zKCJjb3VudCIpLCBuYW1lc190byA9ICJ3aG9sZV92c19zdWJzZXQiLCB2YWx1ZXNfdG8gPSAiY291bnQiKSAlPiUgCiAgZ2dwbG90KGFlcyh5ID0gY2F0ZWdvcnksIHggPSBjb3VudCwgZmlsbCA9IHdob2xlX3ZzX3N1YnNldCkpICsKICBnZW9tX2NvbChwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlMigpKQoKCmlvX2dlbmVzX3N1YnNldCAlPiUgCiAgZ2dwbG90KGFlcyh5ID0gc2hvcnRfYW5ub3RhdGlvbikpICsKICBnZW9tX2JhcigpCmBgYAoKQW5kLCBzcGVjaWZpY2FsbHksIHRoZXNlIGFyZSB0aGUgZ2VuZSB0eXBlcyB0aGF0IGFyZSBmb3VuZCBpbiB0aGUgd2hvbGUgSU8gcGFuZWwgYW5kIG5vdCBpbiB0aGUgc3Vic2V0IHBhbmVsOgoKYGBge3J9CnVuaXF1ZShpb19nZW5lcyRzaG9ydF9hbm5vdGF0aW9uKVt3aGljaCghKHVuaXF1ZShpb19nZW5lcyRzaG9ydF9hbm5vdGF0aW9uKSAlaW4lIHVuaXF1ZShpb19nZW5lc19zdWJzZXQkc2hvcnRfYW5ub3RhdGlvbikpKV0KYGBgCgpTbyB0aGUgZ2VuZSB0eXBlcyB3ZSBhcmUgbWlzc2luZyBhcmUgbWFpbmx5IG5vbi1pbW11bmUsIGFwYXJ0IGZyb20gTmV1dHJvcGhpbHMgLSB3aGljaCBzaG91bGRuJ3QgcmVzaWRlIGluIExOcyBhbnl3YXksIHRvIGJlIGZhaXIuCgojIyBUcmFuc2Zvcm1pbmcgdGhlIG9iamVjdHMKCmBgYHtyfQpzZXVyYXQgPC0gU0NUcmFuc2Zvcm0oc2V1cmF0LCBhc3NheSA9ICJYRU5JVU0iLCBjbGlwLnJhbmdlID0gYygtMTAsIDEwKSkKc2V1cmF0X2lvIDwtIFNDVHJhbnNmb3JtKHNldXJhdF9pbywgYXNzYXkgPSAiWEVOSVVNIiwgY2xpcC5yYW5nZSA9IGMoLTEwLCAxMCkpCmBgYAoKIyMgUnVubmluZyBQQ0EKCmBgYHtyfQpzZXVyYXQgPC0gUnVuUENBKHNldXJhdCkKc2V1cmF0X2lvIDwtIFJ1blBDQShzZXVyYXRfaW8pCmBgYAoKTm93IHRoZSBlbGJvdyBwbG90OgoKYGBge3J9CihFbGJvd1Bsb3Qoc2V1cmF0LCA1MCkgKyBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygxLCA2KSkpICsgCiAgKEVsYm93UGxvdChzZXVyYXRfaW8sIDUwKSArIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDEsIDYpKSkKYGBgCgpgYGB7cn0KZGF0YS5mcmFtZShzZXVyYXRAcmVkdWN0aW9ucyRwY2FAc3RkZXYsIHNldXJhdF9pb0ByZWR1Y3Rpb25zJHBjYUBzdGRldikgJT4lIAogIHJlbmFtZShgNUtgID0gInNldXJhdC5yZWR1Y3Rpb25zLnBjYS5zdGRldiIsCiAgICAgICAgIElPID0gInNldXJhdF9pby5yZWR1Y3Rpb25zLnBjYS5zdGRldiIpICU+JQogIG11dGF0ZShQQyA9IDE6NTAsIC5iZWZvcmUgPSBgNUtgKSAlPiUgCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKGA1S2AsIElPKSwgbmFtZXNfdG8gPSAiUGFuZWwiLCB2YWx1ZXNfdG8gPSAiU0QiKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0gUEMsIHkgPSBTRCwgY29sb3VyID0gUGFuZWwpKSArCiAgZ2VvbV9wb2ludCgpICsKICBsYWJzKHRpdGxlID0gIlBDIFN0YW5kYXJkIERldmlhdGlvbnMiKQpgYGAKCgpXZSB3aWxsIHRha2UgdGhlIGZpcnN0IDI1IFBDcyBmb3IgYm90aCBvYmplY3RzLCBidXQgdGhlcmUgaXMgbGVzcyBjbGVhciBpbmZsZXhpb24gaW4gdGhlIHNtYWxsZXIgcGFuZWwuCgpgYGB7cn0KKEltYWdlRmVhdHVyZVBsb3Qoc2V1cmF0LCAiUENfMSIpICsgc2NhbGVfZmlsbF92aXJpZGlzX2MoKSArIGxhYnModGl0bGUgPSAiNUsiKSkgKwooSW1hZ2VGZWF0dXJlUGxvdChzZXVyYXRfaW8sICJQQ18xIikgKyBzY2FsZV9maWxsX3ZpcmlkaXNfYygpICsgbGFicyh0aXRsZSA9ICJJTyIpKQpgYGAKCmBgYHtyfQoKYGBgCgojIyBSdW4gVU1BUHMKCmBgYHtyfQpzZXVyYXQgPC0gUnVuVU1BUChzZXVyYXQsIGRpbXMgPSAxOjI1KQpzZXVyYXQgPC0gRmluZE5laWdoYm9ycyhzZXVyYXQsIHJlZHVjdGlvbiA9ICJwY2EiLCBkaW1zID0gMToyNSkKc2V1cmF0IDwtIEZpbmRDbHVzdGVycyhzZXVyYXQsIHJlc29sdXRpb24gPSAwLjQpCmBgYAoKYGBge3J9CnNldXJhdF9pbyA8LSBSdW5VTUFQKHNldXJhdF9pbywgZGltcyA9IDE6MjUpCnNldXJhdF9pbyA8LSBGaW5kTmVpZ2hib3JzKHNldXJhdF9pbywgcmVkdWN0aW9uID0gInBjYSIsIGRpbXMgPSAxOjI1KQpzZXVyYXRfaW8gPC0gRmluZENsdXN0ZXJzKHNldXJhdF9pbywgcmVzb2x1dGlvbiA9IDAuNCkKYGBgCgojIyBDb21wYXJlIFVNQVAgcGxvdHMgYW5kIGNsdXN0ZXJpbmcKCmBgYHtyfQpEaW1QbG90KHNldXJhdCwgbGFiZWw9VCwgcmVwZWw9VCkgKyBsYWJzKHRpdGxlID0gIjVLIikKRGltUGxvdChzZXVyYXRfaW8sIGxhYmVsPVQsIHJlcGVsPVQpICArIGxhYnModGl0bGUgPSAiSU8iKQpgYGAKCmBgYHtyfQpEaW1QbG90KHNldXJhdCwgZ3JvdXAuYnkgPSAiQ2VsbHR5cGUiLCBsYWJlbD1ULCByZXBlbD1UKSArIAogIGxhYnModGl0bGUgPSAiNUsiKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygnSGVtYXRvcG9pZXRpYyBTdGVtIENlbGwnID0gJyM4MkEwQkMnLAogICAgICAgICAgICAgICAgICAgICdNYXN0IENlbGwnID0gJyNGQjdBMTMnLAogICAgICAgICAgICAgICAgICAgICdOZXV0cm9waGlsJyA9ICcjMDZENkEwJywKICAgICAgICAgICAgICAgICAgICAiTm9uIENsYXNzaWNhbCBNb25vY3l0ZSIgPSAnIzAwQThFOCcsCiAgICAgICAgICAgICAgICAgICAgJ0ludGVybWVkaWF0ZSBNb25vY3l0ZScgPSAnIzAwN0VBNycsCiAgICAgICAgICAgICAgICAgICAgJ0NsYXNzaWNhbCBNb25vY3l0ZScgPSAnIzAwMzQ1OScsCiAgICAgICAgICAgICAgICAgICAgIk1hY3JvcGhhZ2UiID0gJyM1QTE4OUEnLAogICAgICAgICAgICAgICAgICAgICdFcnl0aHJvY3l0ZScgPSAnI0MxMTIxRicsCiAgICAgICAgICAgICAgICAgICAgJ1BsYXNtYSBDZWxsJyA9ICcjQzFCNzkzJywKICAgICAgICAgICAgICAgICAgICAnVCBDZWxsJyA9ICcjN0Q2NzZEJywKICAgICAgICAgICAgICAgICAgICAiQ0Q0KyDOsc6yIE1lbW9yeSBUIENlbGwiID0gJyNENUJEQUYnLAogICAgICAgICAgICAgICAgICAgICJFZmZlY3RvciBDRDgrIM6xzrIgVCBDZWxsIiA9ICcjQTk5MjdEJywKICAgICAgICAgICAgICAgICAgICAiUmVndWxhdG9yeSBUIENlbGwiID0gJyNFMEFGQTAnLAogICAgICAgICAgICAgICAgICAgICJFZmZlY3RvciBDRDQrIM6xzrIgVCBDZWxsIiA9ICcjQzBDMEMwJywKICAgICAgICAgICAgICAgICAgICAiTmFpdmUgVGh5bXVzIERlcml2ZWQgQ0Q0KyDOsc6yIFQgQ2VsbCIgPSAnI0VGQ0ZFMycsCiAgICAgICAgICAgICAgICAgICAgIkNEOCsgzrHOsiBNZW1vcnkgVCBDZWxsIiA9ICcjOTM4MUZGJywKICAgICAgICAgICAgICAgICAgICAiU3Ryb21hbCBDZWxsIiA9ICcjRkNBMzExJywKICAgICAgICAgICAgICAgICAgICAnTWF0dXJlIENvbnZlbnRpb25hbCBEZW5kcml0aWMgQ2VsbCcgPSAnIzk5NTgyQScsCiAgICAgICAgICAgICAgICAgICAgJ0NEMWMrIE15ZWxvaWQgRGVuZHJpdGljIENlbGwnID0gJyM0MzI4MTgnLAogICAgICAgICAgICAgICAgICAgICdDRDE0MSsgTXllbG9pZCBEZW5kcml0aWMgQ2VsbCcgPSAnI0JCOTQ1NycsCiAgICAgICAgICAgICAgICAgICAgJ01hdHVyZSBOSyBUIENlbGwnID0gJyNBNEMzQjInLAogICAgICAgICAgICAgICAgICAgICdUeXBlIEkgTksgVCBDZWxsJyA9ICcjNkI5MDgwJywKICAgICAgICAgICAgICAgICAgICAnUGxhc21hY3l0b2lkIERlbmRyaXRpYyBDZWxsJyA9ICcjNjFCNEVDJywKICAgICAgICAgICAgICAgICAgICAnQiBDZWxsJyA9ICcjQjRENUExJywKICAgICAgICAgICAgICAgICAgICAiTmFpdmUgQiBDZWxsIiA9ICcjOTBCRTZEJywKICAgICAgICAgICAgICAgICAgICAiTWVtb3J5IEIgQ2VsbCIgPSAnI0E3Qzk1NycsCiAgICAgICAgICAgICAgICAgICAgIklubmF0ZSBMeW1waG9pZCBDZWxsIiA9ICcjRkY4RkFCJywKICAgICAgICAgICAgICAgICAgICAiRW5kb3RoZWxpYWwgQ2VsbCIgPSAnIzU0MEIwRScpLCBuYS52YWx1ZSA9ICcjRTZFN0U5JykgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKCkRpbVBsb3Qoc2V1cmF0X2lvLCBncm91cC5ieSA9ICJDZWxsdHlwZSIsIGxhYmVsPVQsIHJlcGVsPVQpICsgCiAgbGFicyh0aXRsZSA9ICJJTyIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCdIZW1hdG9wb2lldGljIFN0ZW0gQ2VsbCcgPSAnIzgyQTBCQycsCiAgICAgICAgICAgICAgICAgICAgJ01hc3QgQ2VsbCcgPSAnI0ZCN0ExMycsCiAgICAgICAgICAgICAgICAgICAgJ05ldXRyb3BoaWwnID0gJyMwNkQ2QTAnLAogICAgICAgICAgICAgICAgICAgICJOb24gQ2xhc3NpY2FsIE1vbm9jeXRlIiA9ICcjMDBBOEU4JywKICAgICAgICAgICAgICAgICAgICAnSW50ZXJtZWRpYXRlIE1vbm9jeXRlJyA9ICcjMDA3RUE3JywKICAgICAgICAgICAgICAgICAgICAnQ2xhc3NpY2FsIE1vbm9jeXRlJyA9ICcjMDAzNDU5JywKICAgICAgICAgICAgICAgICAgICAiTWFjcm9waGFnZSIgPSAnIzVBMTg5QScsCiAgICAgICAgICAgICAgICAgICAgJ0VyeXRocm9jeXRlJyA9ICcjQzExMjFGJywKICAgICAgICAgICAgICAgICAgICAnUGxhc21hIENlbGwnID0gJyNDMUI3OTMnLAogICAgICAgICAgICAgICAgICAgICdUIENlbGwnID0gJyM3RDY3NkQnLAogICAgICAgICAgICAgICAgICAgICJDRDQrIM6xzrIgTWVtb3J5IFQgQ2VsbCIgPSAnI0Q1QkRBRicsCiAgICAgICAgICAgICAgICAgICAgIkVmZmVjdG9yIENEOCsgzrHOsiBUIENlbGwiID0gJyNBOTkyN0QnLAogICAgICAgICAgICAgICAgICAgICJSZWd1bGF0b3J5IFQgQ2VsbCIgPSAnI0UwQUZBMCcsCiAgICAgICAgICAgICAgICAgICAgIkVmZmVjdG9yIENENCsgzrHOsiBUIENlbGwiID0gJyNDMEMwQzAnLAogICAgICAgICAgICAgICAgICAgICJOYWl2ZSBUaHltdXMgRGVyaXZlZCBDRDQrIM6xzrIgVCBDZWxsIiA9ICcjRUZDRkUzJywKICAgICAgICAgICAgICAgICAgICAiQ0Q4KyDOsc6yIE1lbW9yeSBUIENlbGwiID0gJyM5MzgxRkYnLAogICAgICAgICAgICAgICAgICAgICJTdHJvbWFsIENlbGwiID0gJyNGQ0EzMTEnLAogICAgICAgICAgICAgICAgICAgICdNYXR1cmUgQ29udmVudGlvbmFsIERlbmRyaXRpYyBDZWxsJyA9ICcjOTk1ODJBJywKICAgICAgICAgICAgICAgICAgICAnQ0QxYysgTXllbG9pZCBEZW5kcml0aWMgQ2VsbCcgPSAnIzQzMjgxOCcsCiAgICAgICAgICAgICAgICAgICAgJ0NEMTQxKyBNeWVsb2lkIERlbmRyaXRpYyBDZWxsJyA9ICcjQkI5NDU3JywKICAgICAgICAgICAgICAgICAgICAnTWF0dXJlIE5LIFQgQ2VsbCcgPSAnI0E0QzNCMicsCiAgICAgICAgICAgICAgICAgICAgJ1R5cGUgSSBOSyBUIENlbGwnID0gJyM2QjkwODAnLAogICAgICAgICAgICAgICAgICAgICdQbGFzbWFjeXRvaWQgRGVuZHJpdGljIENlbGwnID0gJyM2MUI0RUMnLAogICAgICAgICAgICAgICAgICAgICdCIENlbGwnID0gJyNCNEQ1QTEnLAogICAgICAgICAgICAgICAgICAgICJOYWl2ZSBCIENlbGwiID0gJyM5MEJFNkQnLAogICAgICAgICAgICAgICAgICAgICJNZW1vcnkgQiBDZWxsIiA9ICcjQTdDOTU3JywKICAgICAgICAgICAgICAgICAgICAiSW5uYXRlIEx5bXBob2lkIENlbGwiID0gJyNGRjhGQUInLAogICAgICAgICAgICAgICAgICAgICJFbmRvdGhlbGlhbCBDZWxsIiA9ICcjNTQwQjBFJyksIG5hLnZhbHVlID0gJyNFNkU3RTknKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCmBgYHtyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDh9CkltYWdlRGltUGxvdChzZXVyYXQsIGdyb3VwLmJ5ID0gIkNlbGx0eXBlIiwgc2l6ZT0uNSkgKyBsYWJzKHRpdGxlID0gIjVLIikKYGBgCgoKCgpgYGB7cn0KKEltYWdlRGltUGxvdChzZXVyYXQsIHNpemU9LjUpICsgbGFicyh0aXRsZSA9ICI1SyIpKSArIAooSW1hZ2VEaW1QbG90KHNldXJhdF9pbywgc2l6ZSA9IC41KSArIGxhYnModGl0bGUgPSAiSU8iKSkKYGBgCgojIyBSZXBlYXQgdGhlIGFib3ZlIGJ1dCBiZXR0ZXIKCkknbSB0aGlua2luZyBpdCB3b3VsZCBiZSBpbnRlcmVzdGluZyB0byBkbyBhbGwgdGhlIGFib3ZlIG9uIDEwIHJhbmRvbSBnZW5lcywgNTAgcmFuZG9tIGdlbmVzLCAxMDAgYW5kIDIwMC4gV2hhdCBjYW4gaXQgZG8/IQoKYGBge3J9CnRlbiA8LSBzYW1wbGUocm93bmFtZXMoc2V1cmF0KSwgc2l6ZSA9IDEwKQpmaWZ0eSA8LSBzYW1wbGUocm93bmFtZXMoc2V1cmF0KSwgc2l6ZSA9IDUwKQpodW5kcmVkIDwtIHNhbXBsZShyb3duYW1lcyhzZXVyYXQpLCBzaXplID0gMTAwKQp0d29faHVuZHJlZCA8LSBzYW1wbGUocm93bmFtZXMoc2V1cmF0KSwgc2l6ZSA9IDIwMCkKZml2ZV9odW5kcmVkIDwtIHNhbXBsZShyb3duYW1lcyhzZXVyYXQpLCBzaXplID0gNTAwKQpvbmVfdGhvdXNhbmQgPC0gc2FtcGxlKHJvd25hbWVzKHNldXJhdCksIHNpemUgPSAxMDAwKQpgYGAKCmBgYHtyfQpzZXVyYXRfMTAgPC0gc2V1cmF0X2ZyYWdtZW50W3Jvd25hbWVzKHNldXJhdF9mcmFnbWVudCkgJWluJSB0ZW4sXQpzZXVyYXRfMTAgPC0gc2V1cmF0XzEwWyxjb2xTdW1zKHNldXJhdF8xMEBhc3NheXMkWEVOSVVNJGNvdW50cykgPiAwXQpzZXVyYXRfNTAgPC0gc2V1cmF0X2ZyYWdtZW50W3Jvd25hbWVzKHNldXJhdF9mcmFnbWVudCkgJWluJSBmaWZ0eSxdCnNldXJhdF81MCA8LSBzZXVyYXRfNTBbLGNvbFN1bXMoc2V1cmF0XzUwQGFzc2F5cyRYRU5JVU0kY291bnRzKSA+IDBdCnNldXJhdF8xMDAgPC0gc2V1cmF0X2ZyYWdtZW50W3Jvd25hbWVzKHNldXJhdF9mcmFnbWVudCkgJWluJSBodW5kcmVkLF0Kc2V1cmF0XzEwMCA8LSBzZXVyYXRfMTAwWyxjb2xTdW1zKHNldXJhdF8xMDBAYXNzYXlzJFhFTklVTSRjb3VudHMpID4gMF0Kc2V1cmF0XzIwMCA8LSBzZXVyYXRfZnJhZ21lbnRbcm93bmFtZXMoc2V1cmF0X2ZyYWdtZW50KSAlaW4lIHR3b19odW5kcmVkLF0Kc2V1cmF0XzIwMCA8LSBzZXVyYXRfMjAwWyxjb2xTdW1zKHNldXJhdF8yMDBAYXNzYXlzJFhFTklVTSRjb3VudHMpID4gMF0Kc2V1cmF0XzUwMCA8LSBzZXVyYXRfZnJhZ21lbnRbcm93bmFtZXMoc2V1cmF0X2ZyYWdtZW50KSAlaW4lIGZpdmVfaHVuZHJlZCxdCnNldXJhdF81MDAgPC0gc2V1cmF0XzUwMFssY29sU3VtcyhzZXVyYXRfNTAwQGFzc2F5cyRYRU5JVU0kY291bnRzKSA+IDBdCnNldXJhdF8xMDAwIDwtIHNldXJhdF9mcmFnbWVudFtyb3duYW1lcyhzZXVyYXRfZnJhZ21lbnQpICVpbiUgb25lX3Rob3VzYW5kLF0Kc2V1cmF0XzEwMDAgPC0gc2V1cmF0XzEwMDBbLGNvbFN1bXMoc2V1cmF0XzEwMDBAYXNzYXlzJFhFTklVTSRjb3VudHMpID4gMF0KYGBgCgojIyMgVHJhbnNmb3JtaW5nIHRlbiwgZmlmdHksIGV0Yy4KCmBgYHtyfQpzZXVyYXRfb2JqZWN0cyA8LSBsaXN0KHRlbiA9IHNldXJhdF8xMCwKICAgICAgICAgICAgICAgICAgICAgICBmaWZ0eSA9IHNldXJhdF81MCwKICAgICAgICAgICAgICAgICAgICAgICBodW5kcmVkID0gc2V1cmF0XzEwMCwKICAgICAgICAgICAgICAgICAgICAgICB0d29faHVuZHJlZCA9IHNldXJhdF8yMDAsCiAgICAgICAgICAgICAgICAgICAgICAgZml2ZV9odW5kcmVkID0gc2V1cmF0XzUwMCwKICAgICAgICAgICAgICAgICAgICAgICBvbmVfdGhvdXNhbmQgPSBzZXVyYXRfMTAwMCkKCnNldXJhdF9vYmplY3RzIDwtIHNldXJhdF9vYmplY3RzICU+JSAKICBtYXAoXCh4KSBTQ1RyYW5zZm9ybSh4LCBhc3NheSA9ICJYRU5JVU0iLCBjbGlwLnJhbmdlID0gYygtMTAsIDEwKSkpCmBgYAoKIyMjIFJ1bm5pbmcgUENBCgpgYGB7cn0Kc2V1cmF0X29iamVjdHMgPC0gc2V1cmF0X29iamVjdHMgJT4lIAogIG1hcChcKHgpIFJ1blBDQSh4KSkgCmBgYAoKRWxib3dQbG90IHRpbWU6CgpgYGB7cn0Kc2V1cmF0X29iamVjdHMgJT4lIAogIG1hcChcKHgpIEVsYm93UGxvdCh4LCA1MCkgKyBsYWJzKHRpdGxlID0gbmFtZXMoeCkpKSAlPiUgCiAgcGxvdF9ncmlkKHBsb3RsaXN0ID0gLikKYGBgCgpgYGB7cn0Kc2V1cmF0X29iamVjdHMgJT4lIAogIG1hcChcKHgpIEltYWdlRmVhdHVyZVBsb3QoeCwgIlBDXzEiKSArIHNjYWxlX2ZpbGxfdmlyaWRpc19jKCkgKyBsYWJzKHRpdGxlID0gbmFtZXMoeCkpKQpgYGAKCgojIyMgUnVubmluZyBVTUFQcw==